mirror of
https://github.com/kata-containers/kata-containers.git
synced 2025-07-15 16:13:20 +00:00
Merge pull request #4202 from fidencio/topic/second-round-of-backports-for-2.4.1
stable-2.4 | Second round of backports for the 2.4.1 release
This commit is contained in:
commit
b11f7df5ab
@ -6,6 +6,7 @@
|
|||||||
#![allow(unknown_lints)]
|
#![allow(unknown_lints)]
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::os::unix::fs::MetadataExt;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::SystemTime;
|
use std::time::SystemTime;
|
||||||
@ -13,6 +14,7 @@ use std::time::SystemTime;
|
|||||||
use anyhow::{ensure, Context, Result};
|
use anyhow::{ensure, Context, Result};
|
||||||
use async_recursion::async_recursion;
|
use async_recursion::async_recursion;
|
||||||
use nix::mount::{umount, MsFlags};
|
use nix::mount::{umount, MsFlags};
|
||||||
|
use nix::unistd::{Gid, Uid};
|
||||||
use slog::{debug, error, info, warn, Logger};
|
use slog::{debug, error, info, warn, Logger};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use tokio::fs;
|
use tokio::fs;
|
||||||
@ -80,7 +82,8 @@ impl Drop for Storage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn copy(from: impl AsRef<Path>, to: impl AsRef<Path>) -> Result<()> {
|
async fn copy(from: impl AsRef<Path>, to: impl AsRef<Path>) -> Result<()> {
|
||||||
if fs::symlink_metadata(&from).await?.file_type().is_symlink() {
|
let metadata = fs::symlink_metadata(&from).await?;
|
||||||
|
if metadata.file_type().is_symlink() {
|
||||||
// if source is a symlink, create new symlink with same link source. If
|
// if source is a symlink, create new symlink with same link source. If
|
||||||
// the symlink exists, remove and create new one:
|
// the symlink exists, remove and create new one:
|
||||||
if fs::symlink_metadata(&to).await.is_ok() {
|
if fs::symlink_metadata(&to).await.is_ok() {
|
||||||
@ -88,8 +91,15 @@ async fn copy(from: impl AsRef<Path>, to: impl AsRef<Path>) -> Result<()> {
|
|||||||
}
|
}
|
||||||
fs::symlink(fs::read_link(&from).await?, &to).await?;
|
fs::symlink(fs::read_link(&from).await?, &to).await?;
|
||||||
} else {
|
} else {
|
||||||
fs::copy(from, to).await?;
|
fs::copy(&from, &to).await?;
|
||||||
}
|
}
|
||||||
|
// preserve the source uid and gid to the destination.
|
||||||
|
nix::unistd::chown(
|
||||||
|
to.as_ref(),
|
||||||
|
Some(Uid::from_raw(metadata.uid())),
|
||||||
|
Some(Gid::from_raw(metadata.gid())),
|
||||||
|
)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,14 +116,29 @@ impl Storage {
|
|||||||
|
|
||||||
async fn update_target(&self, logger: &Logger, source_path: impl AsRef<Path>) -> Result<()> {
|
async fn update_target(&self, logger: &Logger, source_path: impl AsRef<Path>) -> Result<()> {
|
||||||
let source_file_path = source_path.as_ref();
|
let source_file_path = source_path.as_ref();
|
||||||
|
let metadata = source_file_path.symlink_metadata()?;
|
||||||
|
|
||||||
// if we are creating a directory: just create it, nothing more to do
|
// if we are creating a directory: just create it, nothing more to do
|
||||||
if source_file_path.symlink_metadata()?.file_type().is_dir() {
|
if metadata.file_type().is_dir() {
|
||||||
let dest_file_path = self.make_target_path(&source_file_path)?;
|
let dest_file_path = self.make_target_path(&source_file_path)?;
|
||||||
|
|
||||||
fs::create_dir_all(&dest_file_path)
|
fs::create_dir_all(&dest_file_path)
|
||||||
.await
|
.await
|
||||||
.with_context(|| format!("Unable to mkdir all for {}", dest_file_path.display()))?;
|
.with_context(|| format!("Unable to mkdir all for {}", dest_file_path.display()))?;
|
||||||
|
// set the directory permissions to match the source directory permissions
|
||||||
|
fs::set_permissions(&dest_file_path, metadata.permissions())
|
||||||
|
.await
|
||||||
|
.with_context(|| {
|
||||||
|
format!("Unable to set permissions for {}", dest_file_path.display())
|
||||||
|
})?;
|
||||||
|
// preserve the source directory uid and gid to the destination.
|
||||||
|
nix::unistd::chown(
|
||||||
|
&dest_file_path,
|
||||||
|
Some(Uid::from_raw(metadata.uid())),
|
||||||
|
Some(Gid::from_raw(metadata.gid())),
|
||||||
|
)
|
||||||
|
.with_context(|| format!("Unable to set ownership for {}", dest_file_path.display()))?;
|
||||||
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -504,6 +529,7 @@ mod tests {
|
|||||||
use super::*;
|
use super::*;
|
||||||
use crate::mount::is_mounted;
|
use crate::mount::is_mounted;
|
||||||
use crate::skip_if_not_root;
|
use crate::skip_if_not_root;
|
||||||
|
use nix::unistd::{Gid, Uid};
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
|
||||||
@ -895,20 +921,28 @@ mod tests {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_copy() {
|
async fn test_copy() {
|
||||||
|
skip_if_not_root!();
|
||||||
|
|
||||||
// prepare tmp src/destination
|
// prepare tmp src/destination
|
||||||
let source_dir = tempfile::tempdir().unwrap();
|
let source_dir = tempfile::tempdir().unwrap();
|
||||||
let dest_dir = tempfile::tempdir().unwrap();
|
let dest_dir = tempfile::tempdir().unwrap();
|
||||||
|
let uid = Uid::from_raw(10);
|
||||||
|
let gid = Gid::from_raw(200);
|
||||||
|
|
||||||
// verify copy of a regular file
|
// verify copy of a regular file
|
||||||
let src_file = source_dir.path().join("file.txt");
|
let src_file = source_dir.path().join("file.txt");
|
||||||
let dst_file = dest_dir.path().join("file.txt");
|
let dst_file = dest_dir.path().join("file.txt");
|
||||||
fs::write(&src_file, "foo").unwrap();
|
fs::write(&src_file, "foo").unwrap();
|
||||||
|
nix::unistd::chown(&src_file, Some(uid), Some(gid)).unwrap();
|
||||||
|
|
||||||
copy(&src_file, &dst_file).await.unwrap();
|
copy(&src_file, &dst_file).await.unwrap();
|
||||||
// verify destination:
|
// verify destination:
|
||||||
assert!(!fs::symlink_metadata(dst_file)
|
assert!(!fs::symlink_metadata(&dst_file)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.file_type()
|
.file_type()
|
||||||
.is_symlink());
|
.is_symlink());
|
||||||
|
assert_eq!(fs::metadata(&dst_file).unwrap().uid(), uid.as_raw());
|
||||||
|
assert_eq!(fs::metadata(&dst_file).unwrap().gid(), gid.as_raw());
|
||||||
|
|
||||||
// verify copy of a symlink
|
// verify copy of a symlink
|
||||||
let src_symlink_file = source_dir.path().join("symlink_file.txt");
|
let src_symlink_file = source_dir.path().join("symlink_file.txt");
|
||||||
@ -916,7 +950,7 @@ mod tests {
|
|||||||
tokio::fs::symlink(&src_file, &src_symlink_file)
|
tokio::fs::symlink(&src_file, &src_symlink_file)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
copy(src_symlink_file, &dst_symlink_file).await.unwrap();
|
copy(&src_symlink_file, &dst_symlink_file).await.unwrap();
|
||||||
// verify destination:
|
// verify destination:
|
||||||
assert!(fs::symlink_metadata(&dst_symlink_file)
|
assert!(fs::symlink_metadata(&dst_symlink_file)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
@ -924,6 +958,8 @@ mod tests {
|
|||||||
.is_symlink());
|
.is_symlink());
|
||||||
assert_eq!(fs::read_link(&dst_symlink_file).unwrap(), src_file);
|
assert_eq!(fs::read_link(&dst_symlink_file).unwrap(), src_file);
|
||||||
assert_eq!(fs::read_to_string(&dst_symlink_file).unwrap(), "foo");
|
assert_eq!(fs::read_to_string(&dst_symlink_file).unwrap(), "foo");
|
||||||
|
assert_ne!(fs::metadata(&dst_symlink_file).unwrap().uid(), uid.as_raw());
|
||||||
|
assert_ne!(fs::metadata(&dst_symlink_file).unwrap().gid(), gid.as_raw());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
@ -1069,6 +1105,8 @@ mod tests {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn watch_directory() {
|
async fn watch_directory() {
|
||||||
|
skip_if_not_root!();
|
||||||
|
|
||||||
// Prepare source directory:
|
// Prepare source directory:
|
||||||
// ./tmp/1.txt
|
// ./tmp/1.txt
|
||||||
// ./tmp/A/B/2.txt
|
// ./tmp/A/B/2.txt
|
||||||
@ -1079,7 +1117,9 @@ mod tests {
|
|||||||
|
|
||||||
// A/C is an empty directory
|
// A/C is an empty directory
|
||||||
let empty_dir = "A/C";
|
let empty_dir = "A/C";
|
||||||
fs::create_dir_all(source_dir.path().join(empty_dir)).unwrap();
|
let path = source_dir.path().join(empty_dir);
|
||||||
|
fs::create_dir_all(&path).unwrap();
|
||||||
|
nix::unistd::chown(&path, Some(Uid::from_raw(10)), Some(Gid::from_raw(200))).unwrap();
|
||||||
|
|
||||||
// delay 20 ms between writes to files in order to ensure filesystem timestamps are unique
|
// delay 20 ms between writes to files in order to ensure filesystem timestamps are unique
|
||||||
thread::sleep(Duration::from_millis(20));
|
thread::sleep(Duration::from_millis(20));
|
||||||
@ -1123,7 +1163,9 @@ mod tests {
|
|||||||
|
|
||||||
// create another empty directory A/C/D
|
// create another empty directory A/C/D
|
||||||
let empty_dir = "A/C/D";
|
let empty_dir = "A/C/D";
|
||||||
fs::create_dir_all(source_dir.path().join(empty_dir)).unwrap();
|
let path = source_dir.path().join(empty_dir);
|
||||||
|
fs::create_dir_all(&path).unwrap();
|
||||||
|
nix::unistd::chown(&path, Some(Uid::from_raw(10)), Some(Gid::from_raw(200))).unwrap();
|
||||||
assert_eq!(entry.scan(&logger).await.unwrap(), 1);
|
assert_eq!(entry.scan(&logger).await.unwrap(), 1);
|
||||||
assert!(dest_dir.path().join(empty_dir).exists());
|
assert!(dest_dir.path().join(empty_dir).exists());
|
||||||
}
|
}
|
||||||
|
@ -165,6 +165,7 @@ type cloudHypervisor struct {
|
|||||||
APIClient clhClient
|
APIClient clhClient
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
id string
|
id string
|
||||||
|
devicesIds map[string]string
|
||||||
vmconfig chclient.VmConfig
|
vmconfig chclient.VmConfig
|
||||||
state CloudHypervisorState
|
state CloudHypervisorState
|
||||||
config HypervisorConfig
|
config HypervisorConfig
|
||||||
@ -360,6 +361,7 @@ func (clh *cloudHypervisor) CreateVM(ctx context.Context, id string, network Net
|
|||||||
|
|
||||||
clh.id = id
|
clh.id = id
|
||||||
clh.state.state = clhNotReady
|
clh.state.state = clhNotReady
|
||||||
|
clh.devicesIds = make(map[string]string)
|
||||||
|
|
||||||
clh.Logger().WithField("function", "CreateVM").Info("creating Sandbox")
|
clh.Logger().WithField("function", "CreateVM").Info("creating Sandbox")
|
||||||
|
|
||||||
@ -667,7 +669,6 @@ func (clh *cloudHypervisor) hotplugAddBlockDevice(drive *config.BlockDrive) erro
|
|||||||
clhDisk := *chclient.NewDiskConfig(drive.File)
|
clhDisk := *chclient.NewDiskConfig(drive.File)
|
||||||
clhDisk.Readonly = &drive.ReadOnly
|
clhDisk.Readonly = &drive.ReadOnly
|
||||||
clhDisk.VhostUser = func(b bool) *bool { return &b }(false)
|
clhDisk.VhostUser = func(b bool) *bool { return &b }(false)
|
||||||
clhDisk.Id = &driveID
|
|
||||||
|
|
||||||
pciInfo, _, err := cl.VmAddDiskPut(ctx, clhDisk)
|
pciInfo, _, err := cl.VmAddDiskPut(ctx, clhDisk)
|
||||||
|
|
||||||
@ -675,6 +676,7 @@ func (clh *cloudHypervisor) hotplugAddBlockDevice(drive *config.BlockDrive) erro
|
|||||||
return fmt.Errorf("failed to hotplug block device %+v %s", drive, openAPIClientError(err))
|
return fmt.Errorf("failed to hotplug block device %+v %s", drive, openAPIClientError(err))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clh.devicesIds[driveID] = pciInfo.GetId()
|
||||||
drive.PCIPath, err = clhPciInfoToPath(pciInfo)
|
drive.PCIPath, err = clhPciInfoToPath(pciInfo)
|
||||||
|
|
||||||
return err
|
return err
|
||||||
@ -688,11 +690,11 @@ func (clh *cloudHypervisor) hotPlugVFIODevice(device *config.VFIODev) error {
|
|||||||
// Create the clh device config via the constructor to ensure default values are properly assigned
|
// Create the clh device config via the constructor to ensure default values are properly assigned
|
||||||
clhDevice := *chclient.NewVmAddDevice()
|
clhDevice := *chclient.NewVmAddDevice()
|
||||||
clhDevice.Path = &device.SysfsDev
|
clhDevice.Path = &device.SysfsDev
|
||||||
clhDevice.Id = &device.ID
|
|
||||||
pciInfo, _, err := cl.VmAddDevicePut(ctx, clhDevice)
|
pciInfo, _, err := cl.VmAddDevicePut(ctx, clhDevice)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Failed to hotplug device %+v %s", device, openAPIClientError(err))
|
return fmt.Errorf("Failed to hotplug device %+v %s", device, openAPIClientError(err))
|
||||||
}
|
}
|
||||||
|
clh.devicesIds[device.ID] = pciInfo.GetId()
|
||||||
|
|
||||||
// clh doesn't use bridges, so the PCI path is simply the slot
|
// clh doesn't use bridges, so the PCI path is simply the slot
|
||||||
// number of the device. This will break if clh starts using
|
// number of the device. This will break if clh starts using
|
||||||
@ -753,13 +755,15 @@ func (clh *cloudHypervisor) HotplugRemoveDevice(ctx context.Context, devInfo int
|
|||||||
ctx, cancel := context.WithTimeout(context.Background(), clhHotPlugAPITimeout*time.Second)
|
ctx, cancel := context.WithTimeout(context.Background(), clhHotPlugAPITimeout*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
|
originalDeviceID := clh.devicesIds[deviceID]
|
||||||
remove := *chclient.NewVmRemoveDevice()
|
remove := *chclient.NewVmRemoveDevice()
|
||||||
remove.Id = &deviceID
|
remove.Id = &originalDeviceID
|
||||||
_, err := cl.VmRemoveDevicePut(ctx, remove)
|
_, err := cl.VmRemoveDevicePut(ctx, remove)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("failed to hotplug remove (unplug) device %+v: %s", devInfo, openAPIClientError(err))
|
err = fmt.Errorf("failed to hotplug remove (unplug) device %+v: %s", devInfo, openAPIClientError(err))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
delete(clh.devicesIds, deviceID)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -384,6 +384,7 @@ func TestCloudHypervisorHotplugAddBlockDevice(t *testing.T) {
|
|||||||
clh := &cloudHypervisor{}
|
clh := &cloudHypervisor{}
|
||||||
clh.config = clhConfig
|
clh.config = clhConfig
|
||||||
clh.APIClient = &clhClientMock{}
|
clh.APIClient = &clhClientMock{}
|
||||||
|
clh.devicesIds = make(map[string]string)
|
||||||
|
|
||||||
clh.config.BlockDeviceDriver = config.VirtioBlock
|
clh.config.BlockDeviceDriver = config.VirtioBlock
|
||||||
err = clh.hotplugAddBlockDevice(&config.BlockDrive{Pmem: false})
|
err = clh.hotplugAddBlockDevice(&config.BlockDrive{Pmem: false})
|
||||||
@ -406,6 +407,7 @@ func TestCloudHypervisorHotplugRemoveDevice(t *testing.T) {
|
|||||||
clh := &cloudHypervisor{}
|
clh := &cloudHypervisor{}
|
||||||
clh.config = clhConfig
|
clh.config = clhConfig
|
||||||
clh.APIClient = &clhClientMock{}
|
clh.APIClient = &clhClientMock{}
|
||||||
|
clh.devicesIds = make(map[string]string)
|
||||||
|
|
||||||
_, err = clh.HotplugRemoveDevice(context.Background(), &config.BlockDrive{}, BlockDev)
|
_, err = clh.HotplugRemoveDevice(context.Background(), &config.BlockDrive{}, BlockDev)
|
||||||
assert.NoError(err, "Hotplug remove block device expected no error")
|
assert.NoError(err, "Hotplug remove block device expected no error")
|
||||||
|
Loading…
Reference in New Issue
Block a user