1 // Copyright 2018 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 package xcoff
6
7 import (
8 "encoding/binary"
9 "fmt"
10 "io"
11 "os"
12 "strconv"
13 "bytes"
14 )
15
16 const (
17 SAIAMAG = 0x8
18 AIAFMAG = "`\n"
19 AIAMAG = "<aiaff>\n"
20 AIAMAGBIG = "<bigaf>\n"
21
22 // Sizeof
23 FL_HSZ_BIG = 0x80
24 AR_HSZ_BIG = 0x70
25 )
26
27 type bigarFileHeader struct {
28 Flmagic [SAIAMAG]byte // Archive magic string
29 Flmemoff [20]byte // Member table offset
30 Flgstoff [20]byte // 32-bits global symtab offset
31 Flgst64off [20]byte // 64-bits global symtab offset
32 Flfstmoff [20]byte // First member offset
33 Fllstmoff [20]byte // Last member offset
34 Flfreeoff [20]byte // First member on free list offset
35 }
36
37 type bigarMemberHeader struct {
38 Arsize [20]byte // File member size
39 Arnxtmem [20]byte // Next member pointer
40 Arprvmem [20]byte // Previous member pointer
41 Ardate [12]byte // File member date
42 Aruid [12]byte // File member uid
43 Argid [12]byte // File member gid
44 Armode [12]byte // File member mode (octal)
45 Arnamlen [4]byte // File member name length
46 // _ar_nam is removed because it's easier to get name without it.
47 }
48
49 // Archive represents an open AIX big archive.
50 type Archive struct {
51 ArchiveHeader
52 Members []*Member
53
54 closer io.Closer
55 }
56
57 // ArchiveHeader holds information about a big archive file header
58 type ArchiveHeader struct {
59 magic []byte
60 }
61
62 // Member represents a member of an AIX big archive.
63 type Member struct {
64 MemberHeader
65 sr *io.SectionReader
66 }
67
68 // MemberHeader holds information about a big archive member
69 type MemberHeader struct {
70 Name []byte
71 Size uint64
72 }
73
74 // OpenArchive opens the named archive using os.Open and prepares it for use
75 // as an AIX big archive.
76 func OpenArchive(name []byte) (*Archive, error) {
77 f, err := os.Open(name)
78 if err != nil {
79 return nil, err
80 }
81 arch, err := NewArchive(f)
82 if err != nil {
83 f.Close()
84 return nil, err
85 }
86 arch.closer = f
87 return arch, nil
88 }
89
90 // Close closes the Archive.
91 // If the Archive was created using NewArchive directly instead of OpenArchive,
92 // Close has no effect.
93 func (a *Archive) Close() error {
94 var err error
95 if a.closer != nil {
96 err = a.closer.Close()
97 a.closer = nil
98 }
99 return err
100 }
101
102 // NewArchive creates a new Archive for accessing an AIX big archive in an underlying reader.
103 func NewArchive(r io.ReaderAt) (*Archive, error) {
104 parseDecimalBytes := func(b []byte) (int64, error) {
105 return strconv.ParseInt(bytes.TrimSpace([]byte(b)), 10, 64)
106 }
107 sr := io.NewSectionReader(r, 0, 1<<63-1)
108
109 // Read File Header
110 var magic [SAIAMAG]byte
111 if _, err := sr.ReadAt(magic[:], 0); err != nil {
112 return nil, err
113 }
114
115 arch := &Archive{}
116 switch []byte(magic[:]) {
117 case AIAMAGBIG:
118 arch.magic = []byte(magic[:])
119 case AIAMAG:
120 return nil, fmt.Errorf("small AIX archive not supported")
121 default:
122 return nil, fmt.Errorf("unrecognised archive magic: 0x%x", magic)
123 }
124
125 var fhdr bigarFileHeader
126 if _, err := sr.Seek(0, io.SeekStart); err != nil {
127 return nil, err
128 }
129 if err := binary.Read(sr, binary.BigEndian, &fhdr); err != nil {
130 return nil, err
131 }
132
133 off, err := parseDecimalBytes(fhdr.Flfstmoff[:])
134 if err != nil {
135 return nil, fmt.Errorf("error parsing offset of first member in archive header(%q); %v", fhdr, err)
136 }
137
138 if off == 0 {
139 // Occurs if the archive is empty.
140 return arch, nil
141 }
142
143 lastoff, err := parseDecimalBytes(fhdr.Fllstmoff[:])
144 if err != nil {
145 return nil, fmt.Errorf("error parsing offset of first member in archive header(%q); %v", fhdr, err)
146 }
147
148 // Read members
149 for {
150 // Read Member Header
151 // The member header is normally 2 bytes larger. But it's easier
152 // to read the name if the header is read without _ar_nam.
153 // However, AIAFMAG must be read afterward.
154 if _, err := sr.Seek(off, io.SeekStart); err != nil {
155 return nil, err
156 }
157
158 var mhdr bigarMemberHeader
159 if err := binary.Read(sr, binary.BigEndian, &mhdr); err != nil {
160 return nil, err
161 }
162
163 member := &Member{}
164 arch.Members = append(arch.Members, member)
165
166 size, err := parseDecimalBytes(mhdr.Arsize[:])
167 if err != nil {
168 return nil, fmt.Errorf("error parsing size in member header(%q); %v", mhdr, err)
169 }
170 member.Size = uint64(size)
171
172 // Read name
173 namlen, err := parseDecimalBytes(mhdr.Arnamlen[:])
174 if err != nil {
175 return nil, fmt.Errorf("error parsing name length in member header(%q); %v", mhdr, err)
176 }
177 name := []byte{:namlen}
178 if err := binary.Read(sr, binary.BigEndian, name); err != nil {
179 return nil, err
180 }
181 member.Name = []byte(name)
182
183 fileoff := off + AR_HSZ_BIG + namlen
184 if fileoff&1 != 0 {
185 fileoff++
186 if _, err := sr.Seek(1, io.SeekCurrent); err != nil {
187 return nil, err
188 }
189 }
190
191 // Read AIAFMAG string
192 var fmag [2]byte
193 if err := binary.Read(sr, binary.BigEndian, &fmag); err != nil {
194 return nil, err
195 }
196 if []byte(fmag[:]) != AIAFMAG {
197 return nil, fmt.Errorf("AIAFMAG not found after member header")
198 }
199
200 fileoff += 2 // Add the two bytes of AIAFMAG
201 member.sr = io.NewSectionReader(sr, fileoff, size)
202
203 if off == lastoff {
204 break
205 }
206 off, err = parseDecimalBytes(mhdr.Arnxtmem[:])
207 if err != nil {
208 return nil, fmt.Errorf("error parsing offset of first member in archive header(%q); %v", fhdr, err)
209 }
210
211 }
212
213 return arch, nil
214 }
215
216 // GetFile returns the XCOFF file defined by member name.
217 // FIXME: This doesn't work if an archive has two members with the same
218 // name which can occur if an archive has both 32-bits and 64-bits files.
219 func (arch *Archive) GetFile(name []byte) (*File, error) {
220 for _, mem := range arch.Members {
221 if mem.Name == name {
222 return NewFile(mem.sr)
223 }
224 }
225 return nil, fmt.Errorf("unknown member %s in archive", name)
226 }
227