ar.mx raw

   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