github.com/Cloud-Foundations/Dominator@v0.3.4/lib/filesystem/util/replaceComputedFiles.go (about)

     1  package util
     2  
     3  import (
     4  	"bytes"
     5  	"crypto/sha512"
     6  	"errors"
     7  	"fmt"
     8  	"io"
     9  	"io/ioutil"
    10  	"os"
    11  	"path"
    12  	"time"
    13  
    14  	"github.com/Cloud-Foundations/Dominator/lib/filesystem"
    15  	"github.com/Cloud-Foundations/Dominator/lib/hash"
    16  	"github.com/Cloud-Foundations/Dominator/lib/objectserver"
    17  )
    18  
    19  type combinedObjectsGetter struct {
    20  	filenameToHash map[string]hash.Hash
    21  	hashToData     map[hash.Hash][]byte
    22  	hashToFile     map[hash.Hash]string
    23  	hashToSize     map[hash.Hash]uint64
    24  	objectsGetter  objectserver.ObjectsGetter
    25  }
    26  
    27  type combinedObjectsReader struct {
    28  	hashes        []hash.Hash
    29  	objectsGetter *combinedObjectsGetter
    30  	objectsReader objectserver.ObjectsReader
    31  }
    32  
    33  func replaceComputedFiles(fs *filesystem.FileSystem,
    34  	computedFilesData *ComputedFilesData,
    35  	objectsGetter objectserver.ObjectsGetter) (
    36  	objectserver.ObjectsGetter, error) {
    37  	newObjectsGetter, err := makeCombinedObjectsGetter(computedFilesData,
    38  		objectsGetter)
    39  	if err != nil {
    40  		return nil, err
    41  	}
    42  	err = replaceInDirectory(fs, &fs.DirectoryInode, "/", newObjectsGetter)
    43  	if err != nil {
    44  		return nil, err
    45  	}
    46  	return newObjectsGetter, nil
    47  }
    48  
    49  func replaceInDirectory(fs *filesystem.FileSystem,
    50  	directory *filesystem.DirectoryInode, dirname string,
    51  	objectsGetter *combinedObjectsGetter) error {
    52  	for _, entry := range directory.EntryList {
    53  		gInode := entry.Inode()
    54  		if inode, ok := gInode.(*filesystem.DirectoryInode); ok {
    55  			err := replaceInDirectory(fs, inode, path.Join(dirname, entry.Name),
    56  				objectsGetter)
    57  			if err != nil {
    58  				return err
    59  			}
    60  		} else if inode, ok := gInode.(*filesystem.ComputedRegularInode); ok {
    61  			filename := path.Join(dirname, entry.Name)
    62  			hashVal, ok := objectsGetter.filenameToHash[filename]
    63  			if !ok {
    64  				return fmt.Errorf("missing computed file: %s", filename)
    65  			}
    66  			fInode := &filesystem.RegularInode{
    67  				Mode:         inode.Mode,
    68  				Uid:          inode.Uid,
    69  				Gid:          inode.Gid,
    70  				MtimeSeconds: time.Now().Unix(),
    71  				Size:         objectsGetter.hashToSize[hashVal],
    72  				Hash:         hashVal,
    73  			}
    74  			entry.SetInode(fInode)
    75  			fs.InodeTable[entry.InodeNumber] = fInode
    76  		}
    77  	}
    78  	return nil
    79  }
    80  
    81  func makeCombinedObjectsGetter(computedFilesData *ComputedFilesData,
    82  	objectsGetter objectserver.ObjectsGetter) (*combinedObjectsGetter, error) {
    83  	newObjectsGetter := &combinedObjectsGetter{
    84  		filenameToHash: make(map[string]hash.Hash),
    85  		hashToData:     make(map[hash.Hash][]byte),
    86  		hashToFile:     make(map[hash.Hash]string),
    87  		hashToSize:     make(map[hash.Hash]uint64),
    88  		objectsGetter:  objectsGetter,
    89  	}
    90  	for filename, data := range computedFilesData.FileData {
    91  		checksummer := sha512.New()
    92  		if _, err := checksummer.Write(data); err != nil {
    93  			return nil, err
    94  		}
    95  		var hashVal hash.Hash
    96  		copy(hashVal[:], checksummer.Sum(nil))
    97  		newObjectsGetter.filenameToHash[filename] = hashVal
    98  		newObjectsGetter.hashToData[hashVal] = data
    99  		newObjectsGetter.hashToSize[hashVal] = uint64(len(data))
   100  	}
   101  	if computedFilesData.RootDirectory != "" {
   102  		err := newObjectsGetter.scanDirectory(computedFilesData.RootDirectory,
   103  			"/")
   104  		if err != nil {
   105  			return nil, err
   106  		}
   107  	}
   108  	return newObjectsGetter, nil
   109  }
   110  
   111  func (objectsGetter *combinedObjectsGetter) GetObjects(hashes []hash.Hash) (
   112  	objectserver.ObjectsReader, error) {
   113  	unknownHashes := make([]hash.Hash, 0, len(hashes))
   114  	for _, hashVal := range hashes {
   115  		if _, ok := objectsGetter.hashToData[hashVal]; ok {
   116  			continue
   117  		}
   118  		if _, ok := objectsGetter.hashToFile[hashVal]; ok {
   119  			continue
   120  		}
   121  		unknownHashes = append(unknownHashes, hashVal)
   122  	}
   123  	objectsReader, err := objectsGetter.objectsGetter.GetObjects(unknownHashes)
   124  	if err != nil {
   125  		return nil, err
   126  	}
   127  	return &combinedObjectsReader{hashes, objectsGetter, objectsReader}, nil
   128  }
   129  
   130  func (objectsGetter *combinedObjectsGetter) scanDirectory(realDir string,
   131  	mapDir string) error {
   132  	file, err := os.Open(realDir)
   133  	if err != nil {
   134  		return err
   135  	}
   136  	names, err := file.Readdirnames(-1)
   137  	file.Close()
   138  	if err != nil {
   139  		return err
   140  	}
   141  	for _, name := range names {
   142  		realPath := path.Join(realDir, name)
   143  		mapPath := path.Join(mapDir, name)
   144  		if err := objectsGetter.scanEntry(realPath, mapPath); err != nil {
   145  			return err
   146  		}
   147  	}
   148  	return nil
   149  }
   150  
   151  func (objectsGetter *combinedObjectsGetter) scanEntry(realFilename string,
   152  	mapFilename string) error {
   153  	if fi, err := os.Lstat(realFilename); err != nil {
   154  		return err
   155  	} else if fi.IsDir() {
   156  		return objectsGetter.scanDirectory(realFilename, mapFilename)
   157  	} else if fi.Mode()&os.ModeType != 0 {
   158  		return errors.New(realFilename + ": is not a directory or regular file")
   159  	}
   160  	if file, err := os.Open(realFilename); err != nil {
   161  		return err
   162  	} else {
   163  		defer file.Close()
   164  		checksummer := sha512.New()
   165  		nWritten, err := io.Copy(checksummer, file)
   166  		if err != nil {
   167  			return err
   168  		}
   169  		var hashVal hash.Hash
   170  		copy(hashVal[:], checksummer.Sum(nil))
   171  		objectsGetter.filenameToHash[mapFilename] = hashVal
   172  		objectsGetter.hashToFile[hashVal] = realFilename
   173  		objectsGetter.hashToSize[hashVal] = uint64(nWritten)
   174  	}
   175  	return nil
   176  }
   177  
   178  func (objectsReader *combinedObjectsReader) Close() error {
   179  	return objectsReader.objectsReader.Close()
   180  }
   181  
   182  func (objectsReader *combinedObjectsReader) NextObject() (
   183  	uint64, io.ReadCloser, error) {
   184  	hashVal := objectsReader.hashes[0]
   185  	objectsReader.hashes = objectsReader.hashes[1:]
   186  	if data, ok := objectsReader.objectsGetter.hashToData[hashVal]; ok {
   187  		return objectsReader.objectsGetter.hashToSize[hashVal],
   188  			ioutil.NopCloser(bytes.NewReader(data)), nil
   189  	}
   190  	if filename, ok := objectsReader.objectsGetter.hashToFile[hashVal]; ok {
   191  		if file, err := os.Open(filename); err != nil {
   192  			return 0, nil, err
   193  		} else {
   194  			return objectsReader.objectsGetter.hashToSize[hashVal], file, nil
   195  		}
   196  	}
   197  	return objectsReader.objectsReader.NextObject()
   198  }