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 }