// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Microsoft Compound Document File // Reference: https://www.openoffice.org/sc/compdocfileformat.pdf // ERRATA: The above document says the 0th sector is always 512 bytes into the // file. This is not correct. If SectorSize > 512 bytes then the 0th sector is // SectorSize bytes into the file. package comdoc import ( "bytes" "encoding/binary" "errors" "io" "os" ) // CDF file open for reading or writing type ComDoc struct { File io.ReaderAt Header *Header SectorSize int ShortSectorSize int FirstSector int64 MSAT, SAT, SSAT []SecID Files []DirEnt sectorBuf []byte changed bool rootStorage int // index into files rootFiles []int // index into Files writer *os.File closer io.Closer } // Open a CDF file for reading func ReadPath(path string) (*ComDoc, error) { f, err := os.Open(path) if err != nil { return nil, err } return openFile(f, nil, f) } // Open a CDF file for reading and writing func WritePath(path string) (*ComDoc, error) { f, err := os.OpenFile(path, os.O_RDWR, 0) if err != nil { return nil, err } return openFile(f, f, f) } // Parse an already-open CDF file for reading func ReadFile(reader io.ReaderAt) (*ComDoc, error) { return openFile(reader, nil, nil) } // Parse an already-open CDF file for reading and writing func WriteFile(f *os.File) (*ComDoc, error) { return openFile(f, f, nil) } func openFile(reader io.ReaderAt, writer *os.File, closer io.Closer) (*ComDoc, error) { header := new(Header) r := &ComDoc{ File: reader, Header: header, writer: writer, closer: closer, } sr := io.NewSectionReader(reader, 0, 512) if err := binary.Read(sr, binary.LittleEndian, header); err != nil { return nil, err } if !bytes.Equal(header.Magic[:], fileMagic) { return nil, errors.New("not a compound document file") } if header.ByteOrder != byteOrderMarker { return nil, errors.New("incorrect byte order marker") } if header.SectorSize < 5 || header.SectorSize > 28 || header.ShortSectorSize >= header.SectorSize { return nil, errors.New("unreasonable header values") } r.SectorSize = 1 << header.SectorSize r.ShortSectorSize = 1 << header.ShortSectorSize if r.SectorSize < 512 { r.FirstSector = 512 } else { r.FirstSector = int64(r.SectorSize) } r.sectorBuf = make([]byte, r.SectorSize) if err := r.readMSAT(); err != nil { return nil, err } if err := r.readSAT(); err != nil { return nil, err } if err := r.readShortSAT(); err != nil { return nil, err } if err := r.readDir(); err != nil { return nil, err } return r, nil }