diff --git a/src/runtime/virtcontainers/fs_share_linux.go b/src/runtime/virtcontainers/fs_share_linux.go index 2e80bfcc96..e7296bf4e7 100644 --- a/src/runtime/virtcontainers/fs_share_linux.go +++ b/src/runtime/virtcontainers/fs_share_linux.go @@ -11,6 +11,7 @@ import ( "context" "encoding/hex" "fmt" + "io/fs" "os" "path/filepath" "sync" @@ -239,23 +240,43 @@ func (f *FilesystemShare) ShareFile(ctx context.Context, c *Container, m *Mount) if !caps.IsFsSharingSupported() { f.Logger().Debug("filesystem sharing is not supported, files will be copied") - fileInfo, err := os.Stat(m.Source) - if err != nil { - return nil, err + var ignored bool + srcRoot := filepath.Clean(m.Source) + + walk := func(srcPath string, d fs.DirEntry, err error) error { + + if err != nil { + return err + } + + info, err := d.Info() + if err != nil { + return err + } + + if !(info.Mode().IsRegular() || info.Mode().IsDir() || (info.Mode()&os.ModeSymlink) == os.ModeSymlink) { + f.Logger().WithField("ignored-file", srcPath).Debug("Ignoring non-regular file as FS sharing not supported") + if srcPath == srcRoot { + // Ignore the mount if this is not a regular file (excludes socket, device, ...) as it cannot be handled by + // a simple copy. But this should not be treated as an error, only as a limitation. + ignored = true + return filepath.SkipDir + } + return nil + } + + dstPath := filepath.Join(guestPath, srcPath[len(srcRoot):]) + + return f.sandbox.agent.copyFile(ctx, srcPath, dstPath) } - // Ignore the mount if this is not a regular file (excludes - // directory, socket, device, ...) as it cannot be handled by - // a simple copy. But this should not be treated as an error, - // only as a limitation. - if !fileInfo.Mode().IsRegular() { - f.Logger().WithField("ignored-file", m.Source).Debug("Ignoring non-regular file as FS sharing not supported") + if err := filepath.WalkDir(srcRoot, walk); err != nil { + c.Logger().WithField("failed-file", m.Source).Debugf("failed to copy file to sandbox: %v", err) + return nil, err + } + if ignored { return nil, nil } - - if err := f.sandbox.agent.copyFile(ctx, m.Source, guestPath); err != nil { - return nil, err - } } else { // These mounts are created in the shared dir mountDest := filepath.Join(getMountPath(f.sandbox.ID()), filename) diff --git a/src/runtime/virtcontainers/kata_agent.go b/src/runtime/virtcontainers/kata_agent.go index c8221b1602..275433f62f 100644 --- a/src/runtime/virtcontainers/kata_agent.go +++ b/src/runtime/virtcontainers/kata_agent.go @@ -10,6 +10,7 @@ import ( "encoding/json" "errors" "fmt" + "io/ioutil" "os" "path/filepath" "strconv" @@ -2119,40 +2120,57 @@ func (k *kataAgent) setGuestDateTime(ctx context.Context, tv time.Time) error { func (k *kataAgent) copyFile(ctx context.Context, src, dst string) error { var st unix.Stat_t - err := unix.Stat(src, &st) + err := unix.Lstat(src, &st) if err != nil { return fmt.Errorf("Could not get file %s information: %v", src, err) } - b, err := os.ReadFile(src) - if err != nil { - return fmt.Errorf("Could not read file %s: %v", src, err) + cpReq := &grpc.CopyFileRequest{ + Path: dst, + DirMode: uint32(DirMode), + FileMode: st.Mode, + Uid: int32(st.Uid), + Gid: int32(st.Gid), } - fileSize := int64(len(b)) + var b []byte + + switch sflag := st.Mode & unix.S_IFMT; sflag { + case unix.S_IFREG: + var err error + // TODO: Support incrementail file copying instead of loading whole file into memory + b, err = ioutil.ReadFile(src) + if err != nil { + return fmt.Errorf("Could not read file %s: %v", src, err) + } + cpReq.FileSize = int64(len(b)) + + case unix.S_IFDIR: + + case unix.S_IFLNK: + symlink, err := os.Readlink(src) + if err != nil { + return fmt.Errorf("Could not read symlink %s: %v", src, err) + } + cpReq.Data = []byte(symlink) + + default: + return fmt.Errorf("Unsupported file type: %o", sflag) + } k.Logger().WithFields(logrus.Fields{ "source": src, "dest": dst, }).Debugf("Copying file from host to guest") - cpReq := &grpc.CopyFileRequest{ - Path: dst, - DirMode: uint32(DirMode), - FileMode: uint32(st.Mode), - FileSize: fileSize, - Uid: int32(st.Uid), - Gid: int32(st.Gid), - } - // Handle the special case where the file is empty - if fileSize == 0 { - _, err = k.sendReq(ctx, cpReq) + if cpReq.FileSize == 0 { + _, err := k.sendReq(ctx, cpReq) return err } // Copy file by parts if it's needed - remainingBytes := fileSize + remainingBytes := cpReq.FileSize offset := int64(0) for remainingBytes > 0 { bytesToCopy := int64(len(b))