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

     1  package untar
     2  
     3  import (
     4  	"archive/tar"
     5  	"fmt"
     6  	"io"
     7  	"path"
     8  	"strings"
     9  	"syscall"
    10  
    11  	"github.com/Cloud-Foundations/Dominator/lib/filesystem"
    12  	"github.com/Cloud-Foundations/Dominator/lib/filter"
    13  )
    14  
    15  type decoderData struct {
    16  	nextInodeNumber uint64
    17  	fileSystem      filesystem.FileSystem
    18  	inodeTable      map[string]uint64
    19  	directoryTable  map[string]*filesystem.DirectoryInode
    20  }
    21  
    22  func decode(tarReader *tar.Reader, hasher Hasher, filter *filter.Filter) (
    23  	*filesystem.FileSystem, error) {
    24  	var decoderData decoderData
    25  	decoderData.inodeTable = make(map[string]uint64)
    26  	decoderData.directoryTable = make(map[string]*filesystem.DirectoryInode)
    27  	fileSystem := &decoderData.fileSystem
    28  	fileSystem.InodeTable = make(filesystem.InodeTable)
    29  	// Create a default top-level directory which may be updated.
    30  	decoderData.addInode("/", &fileSystem.DirectoryInode)
    31  	fileSystem.DirectoryInode.Mode = syscall.S_IFDIR | syscall.S_IRWXU |
    32  		syscall.S_IRGRP | syscall.S_IXGRP | syscall.S_IROTH | syscall.S_IXOTH
    33  	decoderData.directoryTable["/"] = &fileSystem.DirectoryInode
    34  	for {
    35  		header, err := tarReader.Next()
    36  		if err == io.EOF {
    37  			break
    38  		}
    39  		if err != nil {
    40  			return nil, err
    41  		}
    42  		header.Name = normaliseFilename(header.Name)
    43  		if header.Name == "/.subd" ||
    44  			strings.HasPrefix(header.Name, "/.subd/") {
    45  			continue
    46  		}
    47  		if filter != nil && filter.Match(header.Name) {
    48  			continue
    49  		}
    50  		err = decoderData.addHeader(tarReader, hasher, header)
    51  		if err != nil {
    52  			return nil, err
    53  		}
    54  	}
    55  	delete(fileSystem.InodeTable, 0)
    56  	fileSystem.DirectoryCount = uint64(len(decoderData.directoryTable))
    57  	fileSystem.ComputeTotalDataBytes()
    58  	sortDirectory(&fileSystem.DirectoryInode)
    59  	return fileSystem, nil
    60  }
    61  
    62  func normaliseFilename(filename string) string {
    63  	if filename[:2] == "./" {
    64  		filename = filename[1:]
    65  	} else if filename[0] != '/' {
    66  		filename = "/" + filename
    67  	}
    68  	length := len(filename)
    69  	if length > 1 && filename[length-1] == '/' {
    70  		filename = filename[:length-1]
    71  	}
    72  	return filename
    73  }
    74  
    75  func (decoderData *decoderData) addHeader(tarReader *tar.Reader, hasher Hasher,
    76  	header *tar.Header) error {
    77  	parentDir, ok := decoderData.directoryTable[path.Dir(header.Name)]
    78  	if !ok {
    79  		return fmt.Errorf("no parent directory found for: %s", header.Name)
    80  	}
    81  	leafName := path.Base(header.Name)
    82  	if header.Typeflag == tar.TypeReg || header.Typeflag == tar.TypeRegA {
    83  		return decoderData.addRegularFile(tarReader, hasher, header,
    84  			parentDir, leafName)
    85  	} else if header.Typeflag == tar.TypeLink {
    86  		return decoderData.addHardlink(header, parentDir, leafName)
    87  	} else if header.Typeflag == tar.TypeSymlink {
    88  		return decoderData.addSymlink(header, parentDir, leafName)
    89  	} else if header.Typeflag == tar.TypeChar {
    90  		return decoderData.addSpecialFile(header, parentDir, leafName)
    91  	} else if header.Typeflag == tar.TypeBlock {
    92  		return decoderData.addSpecialFile(header, parentDir, leafName)
    93  	} else if header.Typeflag == tar.TypeDir {
    94  		return decoderData.addDirectory(header, parentDir, leafName)
    95  	} else if header.Typeflag == tar.TypeFifo {
    96  		return decoderData.addSpecialFile(header, parentDir, leafName)
    97  	} else {
    98  		return fmt.Errorf("unsupported file type: %v", header.Typeflag)
    99  	}
   100  }
   101  
   102  func (decoderData *decoderData) addRegularFile(tarReader *tar.Reader,
   103  	hasher Hasher, header *tar.Header, parent *filesystem.DirectoryInode,
   104  	name string) error {
   105  	var newInode filesystem.RegularInode
   106  	newInode.Mode = filesystem.FileMode((header.Mode & ^syscall.S_IFMT) |
   107  		syscall.S_IFREG)
   108  	newInode.Uid = uint32(header.Uid)
   109  	newInode.Gid = uint32(header.Gid)
   110  	newInode.MtimeNanoSeconds = int32(header.ModTime.Nanosecond())
   111  	newInode.MtimeSeconds = header.ModTime.Unix()
   112  	newInode.Size = uint64(header.Size)
   113  	if header.Size > 0 {
   114  		var err error
   115  		newInode.Hash, err = hasher.Hash(tarReader, uint64(header.Size))
   116  		if err != nil {
   117  			return err
   118  		}
   119  	}
   120  	decoderData.addEntry(parent, header.Name, name, &newInode)
   121  	return nil
   122  }
   123  
   124  func (decoderData *decoderData) addDirectory(header *tar.Header,
   125  	parent *filesystem.DirectoryInode, name string) error {
   126  	var newInode filesystem.DirectoryInode
   127  	newInode.Mode = filesystem.FileMode((header.Mode & ^syscall.S_IFMT) |
   128  		syscall.S_IFDIR)
   129  	newInode.Uid = uint32(header.Uid)
   130  	newInode.Gid = uint32(header.Gid)
   131  	if header.Name == "/" {
   132  		*decoderData.directoryTable[header.Name] = newInode
   133  		return nil
   134  	}
   135  	decoderData.addEntry(parent, header.Name, name, &newInode)
   136  	decoderData.directoryTable[header.Name] = &newInode
   137  	return nil
   138  }
   139  
   140  func (decoderData *decoderData) addHardlink(header *tar.Header,
   141  	parent *filesystem.DirectoryInode, name string) error {
   142  	header.Linkname = normaliseFilename(header.Linkname)
   143  	if inum, ok := decoderData.inodeTable[header.Linkname]; ok {
   144  		var newEntry filesystem.DirectoryEntry
   145  		newEntry.Name = name
   146  		newEntry.InodeNumber = inum
   147  		parent.EntryList = append(parent.EntryList, &newEntry)
   148  	} else {
   149  		return fmt.Errorf("missing hardlink target: %s", header.Linkname)
   150  	}
   151  	return nil
   152  }
   153  
   154  func (decoderData *decoderData) addSymlink(header *tar.Header,
   155  	parent *filesystem.DirectoryInode, name string) error {
   156  	var newInode filesystem.SymlinkInode
   157  	newInode.Uid = uint32(header.Uid)
   158  	newInode.Gid = uint32(header.Gid)
   159  	newInode.Symlink = header.Linkname
   160  	decoderData.addEntry(parent, header.Name, name, &newInode)
   161  	return nil
   162  }
   163  
   164  func (decoderData *decoderData) addSpecialFile(header *tar.Header,
   165  	parent *filesystem.DirectoryInode, name string) error {
   166  	var newInode filesystem.SpecialInode
   167  	if header.Typeflag == tar.TypeChar {
   168  		newInode.Mode = filesystem.FileMode((header.Mode & ^syscall.S_IFMT) |
   169  			syscall.S_IFCHR)
   170  	} else if header.Typeflag == tar.TypeBlock {
   171  		newInode.Mode = filesystem.FileMode((header.Mode & ^syscall.S_IFMT) |
   172  			syscall.S_IFBLK)
   173  	} else if header.Typeflag == tar.TypeFifo {
   174  		newInode.Mode = filesystem.FileMode((header.Mode & ^syscall.S_IFMT) |
   175  			syscall.S_IFIFO)
   176  	} else {
   177  		return fmt.Errorf("unsupported type: %v", header.Typeflag)
   178  	}
   179  	newInode.Uid = uint32(header.Uid)
   180  	newInode.Gid = uint32(header.Gid)
   181  	newInode.MtimeNanoSeconds = int32(header.ModTime.Nanosecond())
   182  	newInode.MtimeSeconds = header.ModTime.Unix()
   183  	if header.Devminor > 255 {
   184  		return fmt.Errorf("minor device number: %d too large",
   185  			header.Devminor)
   186  	}
   187  	newInode.Rdev = uint64(header.Devmajor<<8 | header.Devminor)
   188  	decoderData.addEntry(parent, header.Name, name, &newInode)
   189  	return nil
   190  }
   191  
   192  func (decoderData *decoderData) addEntry(parent *filesystem.DirectoryInode,
   193  	fullName, name string, inode filesystem.GenericInode) {
   194  	var newEntry filesystem.DirectoryEntry
   195  	newEntry.Name = name
   196  	newEntry.InodeNumber = decoderData.nextInodeNumber
   197  	newEntry.SetInode(inode)
   198  	parent.EntryList = append(parent.EntryList, &newEntry)
   199  	decoderData.addInode(fullName, inode)
   200  }
   201  
   202  func (decoderData *decoderData) addInode(fullName string,
   203  	inode filesystem.GenericInode) {
   204  	decoderData.inodeTable[fullName] = decoderData.nextInodeNumber
   205  	decoderData.fileSystem.InodeTable[decoderData.nextInodeNumber] = inode
   206  	decoderData.nextInodeNumber++
   207  }