...

Source file src/github.com/sassoftware/relic/lib/comdoc/shortsector.go

Documentation: github.com/sassoftware/relic/lib/comdoc

     1  //
     2  // Copyright (c) SAS Institute Inc.
     3  //
     4  // Licensed under the Apache License, Version 2.0 (the "License");
     5  // you may not use this file except in compliance with the License.
     6  // You may obtain a copy of the License at
     7  //
     8  //     http://www.apache.org/licenses/LICENSE-2.0
     9  //
    10  // Unless required by applicable law or agreed to in writing, software
    11  // distributed under the License is distributed on an "AS IS" BASIS,
    12  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  // See the License for the specific language governing permissions and
    14  // limitations under the License.
    15  //
    16  
    17  package comdoc
    18  
    19  import (
    20  	"bytes"
    21  	"encoding/binary"
    22  	"errors"
    23  )
    24  
    25  // Read the short sector allocation table
    26  //
    27  // The SSAT works the same way as the SAT, except that the indices correspond
    28  // to positions within the short sector stream instead of the file at large,
    29  // and those sectors are ShortSectorSize in length instead of SectorSize. It's
    30  // also stored differently, using the SAT to chain to each subsequent block
    31  // instead of using the MSAT array, in the same way that any other stream works.
    32  func (r *ComDoc) readShortSAT() error {
    33  	count := r.SectorSize / 4
    34  	sat := make([]SecID, count*int(r.Header.SSATSectorCount))
    35  	position := 0
    36  	for sector := r.Header.SSATNextSector; sector >= 0; sector = r.SAT[sector] {
    37  		if position >= len(sat) {
    38  			return errors.New("ssat has more sectors than indicated")
    39  		}
    40  		if err := r.readSectorStruct(sector, sat[position:position+count]); err != nil {
    41  			return err
    42  		}
    43  		position += count
    44  	}
    45  	r.SSAT = sat
    46  	return nil
    47  }
    48  
    49  // Free the old short-sector allocation table and write a new one
    50  func (r *ComDoc) writeShortSAT() error {
    51  	freeSectors(r.SAT, r.Header.SSATNextSector)
    52  	perSector := r.SectorSize / 4
    53  	freeList := r.makeFreeSectors(len(r.SSAT)/perSector, false)
    54  	buf := bytes.NewBuffer(r.sectorBuf)
    55  	first := SecIDEndOfChain
    56  	previous := first
    57  	for i, sector := range freeList {
    58  		j := i * perSector
    59  		chunk := r.SSAT[j : j+perSector]
    60  		buf.Reset()
    61  		binary.Write(buf, binary.LittleEndian, chunk)
    62  		if err := r.writeSector(sector, buf.Bytes()); err != nil {
    63  			return err
    64  		}
    65  		if previous == SecIDEndOfChain {
    66  			first = sector
    67  		} else {
    68  			r.SAT[previous] = sector
    69  		}
    70  		previous = sector
    71  	}
    72  	r.SAT[previous] = SecIDEndOfChain
    73  	r.Header.SSATNextSector = first
    74  	r.Header.SSATSectorCount = uint32(len(freeList))
    75  	return nil
    76  }
    77  
    78  // Read a short sector at the given position.
    79  //
    80  // Short sectors work similarly to big sectors except they are smaller, have
    81  // their own allocation table, and they point to a position within the "short
    82  // sector stream" instead of within the file at large. The short sector stream
    83  // is a regular stream whose first block is pointed to by the root storage
    84  // dirent.
    85  func (r *ComDoc) readShortSector(shortSector SecID, buf []byte) error {
    86  	// figure out which big sector holds the short sector
    87  	bigSectorIndex := int(shortSector) * r.ShortSectorSize / r.SectorSize
    88  	bigSectorID := r.Files[r.rootStorage].NextSector
    89  	for i := 0; i < bigSectorIndex; i++ {
    90  		bigSectorID = r.SAT[bigSectorID]
    91  	}
    92  	// translate to a file position
    93  	n := r.sectorToOffset(bigSectorID)
    94  	n += int64(int(shortSector)*r.ShortSectorSize - bigSectorIndex*r.SectorSize)
    95  	_, err := r.File.ReadAt(buf, n)
    96  	return err
    97  }
    98  
    99  // Write a short sector at the given position. This will allocate new space in
   100  // the short-sector stream if needed.
   101  func (r *ComDoc) writeShortSector(shortSector SecID, content []byte) error {
   102  	if len(content) > r.ShortSectorSize {
   103  		panic("excessive write")
   104  	} else if len(content) < r.ShortSectorSize {
   105  		buf := r.sectorBuf
   106  		copy(buf, content)
   107  		for i := len(content); i < r.ShortSectorSize; i++ {
   108  			buf[i] = 0
   109  		}
   110  		content = buf[:r.ShortSectorSize]
   111  	}
   112  	// walk the short stream to find the big sector that holds the target small sector
   113  	bigSectorIndex := int(shortSector) * r.ShortSectorSize / r.SectorSize
   114  	offset := int(shortSector)*r.ShortSectorSize - bigSectorIndex*r.SectorSize
   115  	root := &r.Files[r.rootStorage]
   116  	bigSectorID := root.NextSector
   117  	for ; bigSectorIndex > 0; bigSectorIndex-- {
   118  		next := r.SAT[bigSectorID]
   119  		if next < 0 {
   120  			break
   121  		}
   122  		bigSectorID = next
   123  	}
   124  	if bigSectorIndex > 0 {
   125  		// extend short sector stream
   126  		freeList := r.makeFreeSectors(bigSectorIndex, false)
   127  		for _, sector := range freeList {
   128  			r.SAT[bigSectorID] = sector
   129  			bigSectorID = sector
   130  		}
   131  		r.SAT[bigSectorID] = SecIDEndOfChain
   132  	}
   133  	n := r.sectorToOffset(bigSectorID) + int64(offset)
   134  	if _, err := r.writer.WriteAt(content, n); err != nil {
   135  		return err
   136  	}
   137  	streamLength := uint32(int(shortSector+1) * r.ShortSectorSize)
   138  	if streamLength > root.StreamSize {
   139  		root.StreamSize = streamLength
   140  	}
   141  	return nil
   142  }
   143  

View as plain text