1
2
3
4
5 package xcoff
6
7 import (
8 "encoding/binary"
9 "fmt"
10 "io"
11 "os"
12 "strconv"
13 "strings"
14 )
15
16 const (
17 SAIAMAG = 0x8
18 AIAFMAG = "`\n"
19 AIAMAG = "<aiaff>\n"
20 AIAMAGBIG = "<bigaf>\n"
21
22
23 FL_HSZ_BIG = 0x80
24 AR_HSZ_BIG = 0x70
25 )
26
27 type bigarFileHeader struct {
28 Flmagic [SAIAMAG]byte
29 Flmemoff [20]byte
30 Flgstoff [20]byte
31 Flgst64off [20]byte
32 Flfstmoff [20]byte
33 Fllstmoff [20]byte
34 Flfreeoff [20]byte
35 }
36
37 type bigarMemberHeader struct {
38 Arsize [20]byte
39 Arnxtmem [20]byte
40 Arprvmem [20]byte
41 Ardate [12]byte
42 Aruid [12]byte
43 Argid [12]byte
44 Armode [12]byte
45 Arnamlen [4]byte
46
47 }
48
49
50 type Archive struct {
51 ArchiveHeader
52 Members []*Member
53
54 closer io.Closer
55 }
56
57
58 type ArchiveHeader struct {
59 magic string
60 }
61
62
63 type Member struct {
64 MemberHeader
65 sr *io.SectionReader
66 }
67
68
69 type MemberHeader struct {
70 Name string
71 Size uint64
72 }
73
74
75
76 func OpenArchive(name string) (*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
91
92
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
103 func NewArchive(r io.ReaderAt) (*Archive, error) {
104 parseDecimalBytes := func(b []byte) (int64, error) {
105 return strconv.ParseInt(strings.TrimSpace(string(b)), 10, 64)
106 }
107 sr := io.NewSectionReader(r, 0, 1<<63-1)
108
109
110 var magic [SAIAMAG]byte
111 if _, err := sr.ReadAt(magic[:], 0); err != nil {
112 return nil, err
113 }
114
115 arch := new(Archive)
116 switch string(magic[:]) {
117 case AIAMAGBIG:
118 arch.magic = string(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
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
149 for {
150
151
152
153
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 := new(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
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 := make([]byte, namlen)
178 if err := binary.Read(sr, binary.BigEndian, name); err != nil {
179 return nil, err
180 }
181 member.Name = string(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
192 var fmag [2]byte
193 if err := binary.Read(sr, binary.BigEndian, &fmag); err != nil {
194 return nil, err
195 }
196 if string(fmag[:]) != AIAFMAG {
197 return nil, fmt.Errorf("AIAFMAG not found after member header")
198 }
199
200 fileoff += 2
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
217
218
219 func (arch *Archive) GetFile(name string) (*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
View as plain text