lxd/storage_zfs.go
func (s *storageZfs) ImageCreate(fingerprint string, tracker *ioprogress.ProgressTracker) error {
	logger.Debugf("Creating ZFS storage volume for image \"%s\" on storage pool \"%s\"", fingerprint, s.pool.Name)

	poolName := s.getOnDiskPoolName()
	imageMntPoint := getImageMountPoint(s.pool.Name, fingerprint)
	fs := fmt.Sprintf("images/%s", fingerprint)
	revert := true
	subrevert := true

	err := s.createImageDbPoolVolume(fingerprint)
	if err != nil {
		return err
	}
	defer func() {
		if !subrevert {
			return
		}
		s.deleteImageDbPoolVolume(fingerprint)
	}()

	if zfsFilesystemEntityExists(poolName, fmt.Sprintf("deleted/%s", fs)) {
		if err := zfsPoolVolumeRename(poolName, fmt.Sprintf("deleted/%s", fs), fs, true); err != nil {
			return err
		}

		defer func() {
			if !revert {
				return
			}
			s.ImageDelete(fingerprint)
		}()

		// In case this is an image from an older lxd instance, wipe the
		// mountpoint.
		err = zfsPoolVolumeSet(poolName, fs, "mountpoint", "none")
		if err != nil {
			return err
		}

		revert = false
		subrevert = false

		return nil
	}

	if !shared.PathExists(imageMntPoint) {
		err := os.MkdirAll(imageMntPoint, 0700)
		if err != nil {
			return err
		}
		defer func() {
			if !subrevert {
				return
			}
			os.RemoveAll(imageMntPoint)
		}()
	}

	// Create temporary mountpoint directory.
	tmp := getImageMountPoint(s.pool.Name, "")
	tmpImageDir, err := ioutil.TempDir(tmp, "")
	if err != nil {
		return err
	}
	defer os.RemoveAll(tmpImageDir)

	imagePath := shared.VarPath("images", fingerprint)

	// Create a new storage volume on the storage pool for the image.
	dataset := fmt.Sprintf("%s/%s", poolName, fs)
	msg, err := zfsPoolVolumeCreate(dataset, "mountpoint=none")
	if err != nil {
		logger.Errorf("Failed to create ZFS dataset \"%s\" on storage pool \"%s\": %s", dataset, s.pool.Name, msg)
		return err
	}
	subrevert = false
	defer func() {
		if !revert {
			return
		}
		s.ImageDelete(fingerprint)
	}()

	// Set a temporary mountpoint for the image.
	err = zfsPoolVolumeSet(poolName, fs, "mountpoint", tmpImageDir)
	if err != nil {
		return err
	}

	// Make sure that the image actually got mounted.
	if !shared.IsMountPoint(tmpImageDir) {
		zfsMount(poolName, fs)
	}

	// Unpack the image into the temporary mountpoint.
	err = unpackImage(imagePath, tmpImageDir, storageTypeZfs, s.s.OS.RunningInUserNS, nil)
	if err != nil {
		return err
	}

	// Mark the new storage volume for the image as readonly.
	if err = zfsPoolVolumeSet(poolName, fs, "readonly", "on"); err != nil {
		return err
	}

	// Remove the temporary mountpoint from the image storage volume.
	if err = zfsPoolVolumeSet(poolName, fs, "mountpoint", "none"); err != nil {
		return err
	}

	// Make sure that the image actually got unmounted.
	if shared.IsMountPoint(tmpImageDir) {
		zfsUmount(poolName, fs, tmpImageDir)
	}

	// Create a snapshot of that image on the storage pool which we clone for
	// container creation.
	err = zfsPoolVolumeSnapshotCreate(poolName, fs, "readonly")
	if err != nil {
		return err
	}

	revert = false

	logger.Debugf("Created ZFS storage volume for image \"%s\" on storage pool \"%s\"", fingerprint, s.pool.Name)
	return nil
}
func (s *storageZfs) ImageCreate(fingerprint string, tracker *ioprogress.ProgressTracker) error {
	logger.Debugf("Creating ZFS storage volume for image \"%s\" on storage pool \"%s\"", fingerprint, s.pool.Name)

	poolName := s.getOnDiskPoolName()
	imageMntPoint := getImageMountPoint(s.pool.Name, fingerprint)
	fs := fmt.Sprintf("images/%s", fingerprint)
	revert := true
	subrevert := true

	err := s.createImageDbPoolVolume(fingerprint)
	if err != nil {
		return err
	}
	defer func() {
		if !subrevert {
			return
		}
		s.deleteImageDbPoolVolume(fingerprint)
	}()

	if zfsFilesystemEntityExists(poolName, fmt.Sprintf("deleted/%s", fs)) {
		if err := zfsPoolVolumeRename(poolName, fmt.Sprintf("deleted/%s", fs), fs, true); err != nil {
			return err
		}

		defer func() {
			if !revert {
				return
			}
			s.ImageDelete(fingerprint)
		}()

		// In case this is an image from an older lxd instance, wipe the
		// mountpoint.
		err = zfsPoolVolumeSet(poolName, fs, "mountpoint", "none")
		if err != nil {
			return err
		}

		revert = false
		subrevert = false

		return nil
	}

	if !shared.PathExists(imageMntPoint) {
		err := os.MkdirAll(imageMntPoint, 0700)
		if err != nil {
			return err
		}
		defer func() {
			if !subrevert {
				return
			}
			os.RemoveAll(imageMntPoint)
		}()
	}

	// Create temporary mountpoint directory.
	tmp := getImageMountPoint(s.pool.Name, "")
	tmpImageDir, err := ioutil.TempDir(tmp, "")
	if err != nil {
		return err
	}
	defer os.RemoveAll(tmpImageDir)

	imagePath := shared.VarPath("images", fingerprint)

	// Create a new storage volume on the storage pool for the image.
	dataset := fmt.Sprintf("%s/%s", poolName, fs)
	msg, err := zfsPoolVolumeCreate(dataset, "mountpoint=none")
	if err != nil {
		logger.Errorf("Failed to create ZFS dataset \"%s\" on storage pool \"%s\": %s", dataset, s.pool.Name, msg)
		return err
	}
	subrevert = false
	defer func() {
		if !revert {
			return
		}
		s.ImageDelete(fingerprint)
	}()

	// Set a temporary mountpoint for the image.
	err = zfsPoolVolumeSet(poolName, fs, "mountpoint", tmpImageDir)
	if err != nil {
		return err
	}

	// Make sure that the image actually got mounted.
	if !shared.IsMountPoint(tmpImageDir) {
		zfsMount(poolName, fs)
	}

	// Unpack the image into the temporary mountpoint.
	err = unpackImage(imagePath, tmpImageDir, storageTypeZfs, s.s.OS.RunningInUserNS, nil)
	if err != nil {
		return err
	}

	// Mark the new storage volume for the image as readonly.
	if err = zfsPoolVolumeSet(poolName, fs, "readonly", "on"); err != nil {
		return err
	}

	// Remove the temporary mountpoint from the image storage volume.
	if err = zfsPoolVolumeSet(poolName, fs, "mountpoint", "none"); err != nil {
		return err
	}

	// Make sure that the image actually got unmounted.
	if shared.IsMountPoint(tmpImageDir) {
		zfsUmount(poolName, fs, tmpImageDir)
	}

	// Create a snapshot of that image on the storage pool which we clone for
	// container creation.
	err = zfsPoolVolumeSnapshotCreate(poolName, fs, "readonly")
	if err != nil {
		return err
	}

	revert = false

	logger.Debugf("Created ZFS storage volume for image \"%s\" on storage pool \"%s\"", fingerprint, s.pool.Name)
	return nil
}
func ( s * storageZfs ) ImageCreate ( fingerprint string , tracker * ioprogress . ProgressTracker ) error { logger . Debugf ( " \" \" \" \" " , fingerprint , s . pool . Name ) poolName := s . getOnDiskPoolName ( ) imageMntPoint := getImageMountPoint ( s . pool . Name , fingerprint ) fs := fmt . Sprintf ( " " , fingerprint ) revert := true subrevert := true err := s . createImageDbPoolVolume ( fingerprint ) if err != nil { return err } defer func ( ) { if ! subrevert { return } s . deleteImageDbPoolVolume ( fingerprint ) } ( ) if zfsFilesystemEntityExists ( poolName , fmt . Sprintf ( " " , fs ) ) { if err := zfsPoolVolumeRename ( poolName , fmt . Sprintf ( " " , fs ) , fs , true ) ; err != nil { return err } defer func ( ) { if ! revert { return } s . ImageDelete ( fingerprint ) } ( ) // In case this is an image from an older lxd instance, wipe the // mountpoint. err = zfsPoolVolumeSet ( poolName , fs , " " , " " ) if err != nil { return err } revert = false subrevert = false return nil } if ! shared . PathExists ( imageMntPoint ) { err := os . MkdirAll ( imageMntPoint , 0700 ) if err != nil { return err } defer func ( ) { if ! subrevert { return } os . RemoveAll ( imageMntPoint ) } ( ) } // Create temporary mountpoint directory. tmp := getImageMountPoint ( s . pool . Name , " " ) tmpImageDir , err := ioutil . TempDir ( tmp , " " ) if err != nil { return err } defer os . RemoveAll ( tmpImageDir ) imagePath := shared . VarPath ( " " , fingerprint ) // Create a new storage volume on the storage pool for the image. dataset := fmt . Sprintf ( " " , poolName , fs ) msg , err := zfsPoolVolumeCreate ( dataset , " " ) if err != nil { logger . Errorf ( " \" \" \" \" " , dataset , s . pool . Name , msg ) return err } subrevert = false defer func ( ) { if ! revert { return } s . ImageDelete ( fingerprint ) } ( ) // Set a temporary mountpoint for the image. err = zfsPoolVolumeSet ( poolName , fs , " " , tmpImageDir ) if err != nil { return err } // Make sure that the image actually got mounted. if ! shared . IsMountPoint ( tmpImageDir ) { zfsMount ( poolName , fs ) } // Unpack the image into the temporary mountpoint. err = unpackImage ( imagePath , tmpImageDir , storageTypeZfs , s . s . OS . RunningInUserNS , nil ) if err != nil { return err } // Mark the new storage volume for the image as readonly. if err = zfsPoolVolumeSet ( poolName , fs , " " , " " ) ; err != nil { return err } // Remove the temporary mountpoint from the image storage volume. if err = zfsPoolVolumeSet ( poolName , fs , " " , " " ) ; err != nil { return err } // Make sure that the image actually got unmounted. if shared . IsMountPoint ( tmpImageDir ) { zfsUmount ( poolName , fs , tmpImageDir ) } // Create a snapshot of that image on the storage pool which we clone for // container creation. err = zfsPoolVolumeSnapshotCreate ( poolName , fs , " " ) if err != nil { return err } revert = false logger . Debugf ( " \" \" \" \" " , fingerprint , s . pool . Name ) return nil }
// - create temporary directory ${LXD_DIR}/images/lxd_images_
// - create new zfs volume images/<fingerprint>
// - mount the zfs volume on ${LXD_DIR}/images/lxd_images_
// - unpack the downloaded image in ${LXD_DIR}/images/lxd_images_
// - mark new zfs volume images/<fingerprint> readonly
// - remove mountpoint property from zfs volume images/<fingerprint>
// - create read-write snapshot from zfs volume images/<fingerprint>
- create temporary directory $ { LXD_DIR } / images / lxd_images_ - create new zfs volume images / <fingerprint > - mount the zfs volume on $ { LXD_DIR } / images / lxd_images_ - unpack the downloaded image in $ { LXD_DIR } / images / lxd_images_ - mark new zfs volume images / <fingerprint > readonly - remove mountpoint property from zfs volume images / <fingerprint > - create read - write snapshot from zfs volume images / <fingerprint >