xref: /aosp_15_r20/external/bazelbuild-rules_go/go/tools/builders/ar.go (revision 9bb1b549b6a84214c53be0924760be030e66b93a)
1// Copyright 2018 The Bazel Authors. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//    http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package main
16
17import (
18	"encoding/binary"
19	"fmt"
20	"io"
21	"os"
22	"strconv"
23	"strings"
24)
25
26type header struct {
27	NameRaw     [16]byte
28	ModTimeRaw  [12]byte
29	OwnerIdRaw  [6]byte
30	GroupIdRaw  [6]byte
31	FileModeRaw [8]byte
32	FileSizeRaw [10]byte
33	EndRaw      [2]byte
34}
35
36func (h *header) name() string {
37	return strings.TrimRight(string(h.NameRaw[:]), " ")
38}
39
40func (h *header) size() int64 {
41	s, err := strconv.Atoi(strings.TrimRight(string(h.FileSizeRaw[:]), " "))
42	if err != nil {
43		panic(err)
44	}
45	return int64(s)
46}
47
48func (h *header) next() int64 {
49	size := h.size()
50	return size + size%2
51}
52
53func (h *header) deterministic() *header {
54	h2 := *h
55	copy(h2.ModTimeRaw[:], zeroBytes)
56	copy(h2.OwnerIdRaw[:], zeroBytes)
57	copy(h2.GroupIdRaw[:], zeroBytes)
58	copy(h2.FileModeRaw[:], zeroBytes) // GNU ar also clears this
59	return &h2
60}
61
62// stripArMetadata strips the archive metadata of non-deterministic data:
63// - Timestamps
64// - User IDs
65// - Group IDs
66// - File Modes
67// The archive is modified in place.
68func stripArMetadata(archivePath string) error {
69	archive, err := os.OpenFile(archivePath, os.O_RDWR, 0)
70	if err != nil {
71		return err
72	}
73	defer archive.Close()
74
75	magic := make([]byte, len(arHeader))
76	if _, err := io.ReadFull(archive, magic); err != nil {
77		return err
78	}
79
80	if string(magic) != arHeader {
81		return fmt.Errorf("%s is not an archive", archivePath)
82	}
83
84	for {
85		hdr := &header{}
86		if err := binary.Read(archive, binary.BigEndian, hdr); err == io.EOF {
87			return nil
88		} else if err != nil {
89			return err
90		}
91
92		// Seek back at the beginning of the header and overwrite it.
93		archive.Seek(-entryLength, os.SEEK_CUR)
94		if err := binary.Write(archive, binary.BigEndian, hdr.deterministic()); err != nil {
95			return err
96		}
97
98		if _, err := archive.Seek(hdr.next(), os.SEEK_CUR); err == io.EOF {
99			return nil
100		} else if err != nil {
101			return err
102		}
103	}
104}
105