github.com/cloud-foundations/dominator@v0.0.0-20221004181915-6e4fee580046/imageunpacker/unpacker/prepareForUnpack.go (about)

     1  package unpacker
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"os"
     7  	"path/filepath"
     8  	"time"
     9  
    10  	"github.com/Cloud-Foundations/Dominator/lib/filesystem"
    11  	"github.com/Cloud-Foundations/Dominator/lib/filesystem/scanner"
    12  	"github.com/Cloud-Foundations/Dominator/lib/format"
    13  	"github.com/Cloud-Foundations/Dominator/lib/objectcache"
    14  	"github.com/Cloud-Foundations/Dominator/lib/wsyscall"
    15  	proto "github.com/Cloud-Foundations/Dominator/proto/imageunpacker"
    16  )
    17  
    18  func (u *Unpacker) prepareForUnpack(streamName string, skipIfPrepared bool,
    19  	doNotWaitForResult bool) error {
    20  	u.rwMutex.Lock()
    21  	u.updateUsageTimeWithLock()
    22  	streamInfo, err := u.setupStream(streamName)
    23  	u.rwMutex.Unlock()
    24  	defer u.updateUsageTime()
    25  	if err != nil {
    26  		return err
    27  	}
    28  	errorChannel := make(chan error)
    29  	request := requestType{
    30  		request:        requestScan,
    31  		skipIfPrepared: skipIfPrepared,
    32  		errorChannel:   errorChannel,
    33  	}
    34  	streamInfo.requestChannel <- request
    35  	if doNotWaitForResult {
    36  		go func() {
    37  			<-errorChannel
    38  		}()
    39  		return nil
    40  	}
    41  	return <-errorChannel
    42  }
    43  
    44  func (stream *streamManagerState) scan(skipIfPrepared bool) error {
    45  	if err := stream.getDevice(); err != nil {
    46  		return err
    47  	}
    48  	streamInfo := stream.streamInfo
    49  	if streamInfo.status == proto.StatusStreamNoFileSystem {
    50  		return nil // Nothing to scan.
    51  	}
    52  	mountPoint := filepath.Join(stream.unpacker.baseDir, "mnt")
    53  	if err := stream.mount(mountPoint); err != nil {
    54  		return err
    55  	}
    56  	switch streamInfo.status {
    57  	case proto.StatusStreamNoDevice:
    58  		return errors.New("no device")
    59  	case proto.StatusStreamNotMounted:
    60  		return errors.New("not mounted")
    61  	case proto.StatusStreamMounted:
    62  		// Start scanning.
    63  	case proto.StatusStreamScanning:
    64  		return errors.New("stream scan in progress")
    65  	case proto.StatusStreamScanned:
    66  		if skipIfPrepared {
    67  			return nil
    68  		}
    69  		// Start scanning.
    70  	case proto.StatusStreamFetching:
    71  		return errors.New("fetch in progress")
    72  	case proto.StatusStreamUpdating:
    73  		return errors.New("update in progress")
    74  	case proto.StatusStreamPreparing:
    75  		return errors.New("preparing to capture")
    76  	default:
    77  		panic("invalid status")
    78  	}
    79  	streamInfo.status = proto.StatusStreamScanning
    80  	stream.unpacker.logger.Printf("Initiating scan(%s)\n", stream.streamName)
    81  	startTime := time.Now()
    82  	var err error
    83  	stream.fileSystem, err = stream.scanFS(mountPoint)
    84  	if err != nil {
    85  		return err
    86  	}
    87  	stream.objectCache, err = objectcache.ScanObjectCache(
    88  		filepath.Join(stream.unpacker.baseDir, "mnt", ".subd", "objects"))
    89  	if err != nil {
    90  		return err
    91  	}
    92  	streamInfo.status = proto.StatusStreamScanned
    93  	stream.unpacker.logger.Printf("Scanned(%s) in %s\n",
    94  		stream.streamName, format.Duration(time.Since(startTime)))
    95  	return nil
    96  }
    97  
    98  func (stream *streamManagerState) scanFS(mountPoint string) (
    99  	*filesystem.FileSystem, error) {
   100  	sfs, err := scanner.ScanFileSystem(mountPoint, nil, nil, nil,
   101  		scanner.GetSimpleHasher(false), nil)
   102  	if err != nil {
   103  		return nil, err
   104  	}
   105  	fs := &sfs.FileSystem
   106  	if err := fs.RebuildInodePointers(); err != nil {
   107  		return nil, err
   108  	}
   109  	fs.BuildEntryMap()
   110  	return fs, nil
   111  }
   112  
   113  func (stream *streamManagerState) getDevice() error {
   114  	u := stream.unpacker
   115  	u.rwMutex.Lock()
   116  	defer u.rwMutex.Unlock()
   117  	return stream.getDeviceWithLock()
   118  }
   119  
   120  func (stream *streamManagerState) getDeviceWithLock() error {
   121  	u := stream.unpacker
   122  	streamInfo := stream.streamInfo
   123  	if streamInfo.DeviceId != "" {
   124  		return nil
   125  	}
   126  	// Search for unused device.
   127  	for deviceId, deviceInfo := range u.pState.Devices {
   128  		if deviceInfo.StreamName == "" {
   129  			deviceInfo.StreamName = stream.streamName
   130  			u.pState.Devices[deviceId] = deviceInfo
   131  			streamInfo.DeviceId = deviceId
   132  			streamInfo.status = proto.StatusStreamNoFileSystem
   133  			stream.rootLabel = ""
   134  			if err := u.writeStateWithLock(); err != nil {
   135  				return err
   136  			}
   137  			break
   138  		}
   139  	}
   140  	if streamInfo.DeviceId == "" {
   141  		return errors.New("no available device")
   142  	}
   143  	return nil
   144  }
   145  
   146  func (stream *streamManagerState) mount(mountPoint string) error {
   147  	streamInfo := stream.streamInfo
   148  	switch streamInfo.status {
   149  	case proto.StatusStreamNoDevice:
   150  		panic("no device")
   151  	case proto.StatusStreamNotMounted:
   152  		// Not mounted: go ahead and mount.
   153  	case proto.StatusStreamNoFileSystem:
   154  		panic("no file-system")
   155  	default:
   156  		// Already mounted.
   157  		return nil
   158  	}
   159  	stream.unpacker.rwMutex.RLock()
   160  	device := stream.unpacker.pState.Devices[stream.streamInfo.DeviceId]
   161  	stream.unpacker.rwMutex.RUnlock()
   162  	rootDevice, err := getPartition(filepath.Join("/dev", device.DeviceName))
   163  	if err != nil {
   164  		return err
   165  	}
   166  	err = wsyscall.Mount(rootDevice, mountPoint, "ext4", 0, "")
   167  	if err != nil {
   168  		return fmt.Errorf("error mounting: %s onto: %s: %s", rootDevice,
   169  			mountPoint, err)
   170  	}
   171  	err = os.MkdirAll(filepath.Join(mountPoint, ".subd", "objects"), dirPerms)
   172  	if err != nil {
   173  		return err
   174  	}
   175  	streamInfo.status = proto.StatusStreamMounted
   176  	stream.unpacker.logger.Printf("Mounted(%s) %s\n",
   177  		stream.streamName, rootDevice)
   178  	return nil
   179  }