lxd/container_lxc.go
func (c *containerLXC) createDiskDevice(name string, m types.Device) (string, error) {
	// source paths
	relativeDestPath := strings.TrimPrefix(m["path"], "/")
	devName := fmt.Sprintf("disk.%s.%s", strings.Replace(name, "/", "-", -1), strings.Replace(relativeDestPath, "/", "-", -1))
	devPath := filepath.Join(c.DevicesPath(), devName)
	srcPath := shared.HostPath(m["source"])

	// Check if read-only
	isOptional := shared.IsTrue(m["optional"])
	isReadOnly := shared.IsTrue(m["readonly"])
	isRecursive := shared.IsTrue(m["recursive"])

	isFile := false
	if m["pool"] == "" {
		isFile = !shared.IsDir(srcPath) && !deviceIsBlockdev(srcPath)
	} else {
		// Deal with mounting storage volumes created via the storage
		// api. Extract the name of the storage volume that we are
		// supposed to attach. We assume that the only syntactically
		// valid ways of specifying a storage volume are:
		// - <volume_name>
		// - <type>/<volume_name>
		// Currently, <type> must either be empty or "custom". We do not
		// yet support container mounts.

		if filepath.IsAbs(m["source"]) {
			return "", fmt.Errorf("When the \"pool\" property is set \"source\" must specify the name of a volume, not a path")
		}

		volumeTypeName := ""
		volumeName := filepath.Clean(m["source"])
		slash := strings.Index(volumeName, "/")
		if (slash > 0) && (len(volumeName) > slash) {
			// Extract volume name.
			volumeName = m["source"][(slash + 1):]
			// Extract volume type.
			volumeTypeName = m["source"][:slash]
		}

		switch volumeTypeName {
		case storagePoolVolumeTypeNameContainer:
			return "", fmt.Errorf("Using container storage volumes is not supported")
		case "":
			// We simply received the name of a storage volume.
			volumeTypeName = storagePoolVolumeTypeNameCustom
			fallthrough
		case storagePoolVolumeTypeNameCustom:
			srcPath = shared.VarPath("storage-pools", m["pool"], volumeTypeName, volumeName)
		case storagePoolVolumeTypeNameImage:
			return "", fmt.Errorf("Using image storage volumes is not supported")
		default:
			return "", fmt.Errorf("Unknown storage type prefix \"%s\" found", volumeTypeName)
		}

		// Initialize a new storage interface and check if the
		// pool/volume is mounted. If it is not, mount it.
		volumeType, _ := storagePoolVolumeTypeNameToType(volumeTypeName)
		s, err := storagePoolVolumeAttachInit(c.state, m["pool"], volumeName, volumeType, c)
		if err != nil && !isOptional {
			return "", fmt.Errorf("Failed to initialize storage volume \"%s\" of type \"%s\" on storage pool \"%s\": %s",
				volumeName,
				volumeTypeName,
				m["pool"], err)
		} else if err == nil {
			_, err = s.StoragePoolVolumeMount()
			if err != nil {
				msg := fmt.Sprintf("Could not mount storage volume \"%s\" of type \"%s\" on storage pool \"%s\": %s.",
					volumeName,
					volumeTypeName,
					m["pool"], err)
				if !isOptional {
					logger.Errorf(msg)
					return "", err
				}
				logger.Warnf(msg)
			}
		}
	}

	// Check if the source exists
	if !shared.PathExists(srcPath) {
		if isOptional {
			return "", nil
		}
		return "", fmt.Errorf("Source path %s doesn't exist for device %s", srcPath, name)
	}

	// Create the devices directory if missing
	if !shared.PathExists(c.DevicesPath()) {
		err := os.Mkdir(c.DevicesPath(), 0711)
		if err != nil {
			return "", err
		}
	}

	// Clean any existing entry
	if shared.PathExists(devPath) {
		err := os.Remove(devPath)
		if err != nil {
			return "", err
		}
	}

	// Create the mount point
	if isFile {
		f, err := os.Create(devPath)
		if err != nil {
			return "", err
		}

		f.Close()
	} else {
		err := os.Mkdir(devPath, 0700)
		if err != nil {
			return "", err
		}
	}

	// Mount the fs
	err := deviceMountDisk(srcPath, devPath, isReadOnly, isRecursive, m["propagation"])
	if err != nil {
		return "", err
	}

	return devPath, nil
}
func (c *containerLXC) createDiskDevice(name string, m types.Device) (string, error) {
	// source paths
	relativeDestPath := strings.TrimPrefix(m["path"], "/")
	devName := fmt.Sprintf("disk.%s.%s", strings.Replace(name, "/", "-", -1), strings.Replace(relativeDestPath, "/", "-", -1))
	devPath := filepath.Join(c.DevicesPath(), devName)
	srcPath := shared.HostPath(m["source"])

	// Check if read-only
	isOptional := shared.IsTrue(m["optional"])
	isReadOnly := shared.IsTrue(m["readonly"])
	isRecursive := shared.IsTrue(m["recursive"])

	isFile := false
	if m["pool"] == "" {
		isFile = !shared.IsDir(srcPath) && !deviceIsBlockdev(srcPath)
	} else {
		// Deal with mounting storage volumes created via the storage
		// api. Extract the name of the storage volume that we are
		// supposed to attach. We assume that the only syntactically
		// valid ways of specifying a storage volume are:
		// - <volume_name>
		// - <type>/<volume_name>
		// Currently, <type> must either be empty or "custom". We do not
		// yet support container mounts.

		if filepath.IsAbs(m["source"]) {
			return "", fmt.Errorf("When the \"pool\" property is set \"source\" must specify the name of a volume, not a path")
		}

		volumeTypeName := ""
		volumeName := filepath.Clean(m["source"])
		slash := strings.Index(volumeName, "/")
		if (slash > 0) && (len(volumeName) > slash) {
			// Extract volume name.
			volumeName = m["source"][(slash + 1):]
			// Extract volume type.
			volumeTypeName = m["source"][:slash]
		}

		switch volumeTypeName {
		case storagePoolVolumeTypeNameContainer:
			return "", fmt.Errorf("Using container storage volumes is not supported")
		case "":
			// We simply received the name of a storage volume.
			volumeTypeName = storagePoolVolumeTypeNameCustom
			fallthrough
		case storagePoolVolumeTypeNameCustom:
			srcPath = shared.VarPath("storage-pools", m["pool"], volumeTypeName, volumeName)
		case storagePoolVolumeTypeNameImage:
			return "", fmt.Errorf("Using image storage volumes is not supported")
		default:
			return "", fmt.Errorf("Unknown storage type prefix \"%s\" found", volumeTypeName)
		}

		// Initialize a new storage interface and check if the
		// pool/volume is mounted. If it is not, mount it.
		volumeType, _ := storagePoolVolumeTypeNameToType(volumeTypeName)
		s, err := storagePoolVolumeAttachInit(c.state, m["pool"], volumeName, volumeType, c)
		if err != nil && !isOptional {
			return "", fmt.Errorf("Failed to initialize storage volume \"%s\" of type \"%s\" on storage pool \"%s\": %s",
				volumeName,
				volumeTypeName,
				m["pool"], err)
		} else if err == nil {
			_, err = s.StoragePoolVolumeMount()
			if err != nil {
				msg := fmt.Sprintf("Could not mount storage volume \"%s\" of type \"%s\" on storage pool \"%s\": %s.",
					volumeName,
					volumeTypeName,
					m["pool"], err)
				if !isOptional {
					logger.Errorf(msg)
					return "", err
				}
				logger.Warnf(msg)
			}
		}
	}

	// Check if the source exists
	if !shared.PathExists(srcPath) {
		if isOptional {
			return "", nil
		}
		return "", fmt.Errorf("Source path %s doesn't exist for device %s", srcPath, name)
	}

	// Create the devices directory if missing
	if !shared.PathExists(c.DevicesPath()) {
		err := os.Mkdir(c.DevicesPath(), 0711)
		if err != nil {
			return "", err
		}
	}

	// Clean any existing entry
	if shared.PathExists(devPath) {
		err := os.Remove(devPath)
		if err != nil {
			return "", err
		}
	}

	// Create the mount point
	if isFile {
		f, err := os.Create(devPath)
		if err != nil {
			return "", err
		}

		f.Close()
	} else {
		err := os.Mkdir(devPath, 0700)
		if err != nil {
			return "", err
		}
	}

	// Mount the fs
	err := deviceMountDisk(srcPath, devPath, isReadOnly, isRecursive, m["propagation"])
	if err != nil {
		return "", err
	}

	return devPath, nil
}
// Disk device handling