...

Source file src/internal/xcoff/ar.go

     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		"strings"
    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	// MemberHeader holds information about a big archive file header
    58	type ArchiveHeader struct {
    59		magic string
    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 string
    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 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	// 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(strings.TrimSpace(string(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 := 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, os.SEEK_SET); 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, os.SEEK_SET); 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			// 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 := 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, os.SEEK_CUR); 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 string(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	
   217	// GetFile returns the XCOFF file defined by member name.
   218	// FIXME: This doesn't work if an archive has two members with the same
   219	// name which can occur if a archive has both 32-bits and 64-bits files.
   220	func (arch *Archive) GetFile(name string) (*File, error) {
   221		for _, mem := range arch.Members {
   222			if mem.Name == name {
   223				return NewFile(mem.sr)
   224			}
   225		}
   226		return nil, fmt.Errorf("unknown member %s in archive", name)
   227	
   228	}
   229	

View as plain text