Skip to content
This repository has been archived by the owner on May 12, 2021. It is now read-only.

Commit

Permalink
Merge pull request #433 from devimc/topic/uploadFile
Browse files Browse the repository at this point in the history
protocols/grpc: implement function to copy files
  • Loading branch information
Sebastien Boeuf authored Dec 18, 2018
2 parents 173bb55 + 169d755 commit 8e48125
Show file tree
Hide file tree
Showing 5 changed files with 753 additions and 157 deletions.
76 changes: 76 additions & 0 deletions grpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ var (
sysfsMemOnlinePath = "/sys/devices/system/memory"
sysfsMemoryBlockSizePath = "/sys/devices/system/memory/block_size_bytes"
sysfsConnectedCPUsPath = filepath.Join(sysfsCPUOnlinePath, "online")
containersRootfsPath = "/run"
)

type onlineResource struct {
Expand Down Expand Up @@ -1450,3 +1451,78 @@ func (a *agentGRPC) SetGuestDateTime(ctx context.Context, req *pb.SetGuestDateTi
}
return &gpb.Empty{}, nil
}

// CopyFile copies files form host to container's rootfs (guest). Files can be copied by parts, for example
// a file which size is 2MB, can be copied calling CopyFile 2 times, in the first call req.Offset is 0,
// req.FileSize is 2MB and req.Data contains the first half of the file, in the seconds call req.Offset is 1MB,
// req.FileSize is 2MB and req.Data contains the second half of the file. For security reason all write operations
// are made in a temporary file, once temporary file reaches the expected size (req.FileSize), it's moved to
// destination file (req.Path).
func (a *agentGRPC) CopyFile(ctx context.Context, req *pb.CopyFileRequest) (*gpb.Empty, error) {
// get absolute path, to avoid paths like '/run/../sbin/init'
path, err := filepath.Abs(req.Path)
if err != nil {
return emptyResp, err
}

// container's rootfs is mounted at /run, in order to avoid overwrite guest's rootfs files, only
// is possible to copy files to /run
if !strings.HasPrefix(path, containersRootfsPath) {
return emptyResp, fmt.Errorf("Only is possible to copy files into the %s directory", containersRootfsPath)
}

if err := os.MkdirAll(filepath.Dir(path), os.FileMode(req.DirMode)); err != nil {
return emptyResp, err
}

// create a temporary file and write the content.
tmpPath := path + ".tmp"
tmpFile, err := os.OpenFile(tmpPath, os.O_WRONLY|os.O_CREATE, 0600)
if err != nil {
return emptyResp, err
}

if _, err := tmpFile.WriteAt(req.Data, req.Offset); err != nil {
tmpFile.Close()
return emptyResp, err
}
tmpFile.Close()

// get temporary file information
st, err := os.Stat(tmpPath)
if err != nil {
return emptyResp, err
}

agentLog.WithFields(logrus.Fields{
"tmp-file-size": st.Size(),
"expected-size": req.FileSize,
}).Debugf("Checking temporary file size")

// if file size is not equal to the expected size means that copy file operation has not finished.
// CopyFile should be called again with new content and a different offset.
if st.Size() != req.FileSize {
return emptyResp, nil
}

if err := os.Chmod(tmpPath, os.FileMode(req.FileMode)); err != nil {
return emptyResp, err
}

if err := os.Chown(tmpPath, int(req.Uid), int(req.Gid)); err != nil {
return emptyResp, err
}

// At this point temoporary file has the expected size, atomically move it overwriting
// the destination.
agentLog.WithFields(logrus.Fields{
"tmp-path": tmpPath,
"des-path": path,
}).Debugf("Moving temporary file")

if err := os.Rename(tmpPath, path); err != nil {
return emptyResp, err
}

return emptyResp, nil
}
50 changes: 50 additions & 0 deletions grpc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -786,3 +786,53 @@ func TestPosixRlimitsToRlimits(t *testing.T) {

assert.Equal(rlimits, expectedRlimits)
}

func TestCopyFile(t *testing.T) {
assert := assert.New(t)

oldContainersRootfsPath := containersRootfsPath
containersRootfsPath = "/tmp"
defer func() {
containersRootfsPath = oldContainersRootfsPath
}()

a := &agentGRPC{}
req := &pb.CopyFileRequest{
DirMode: 0755,
FileMode: 0755,
Uid: int32(os.Getuid()),
Gid: int32(os.Getgid()),
}

_, err := a.CopyFile(context.Background(), req)
assert.Error(err)

dir, err := ioutil.TempDir("", "copy")
assert.NoError(err)
defer os.RemoveAll(dir)

req.Path = filepath.Join(dir, "file")

part1 := []byte("hello")
part2 := []byte("world")
req.FileSize = int64(len(part1) + len(part2))

// send first part
req.Offset = 0
req.Data = part1
_, err = a.CopyFile(context.Background(), req)
assert.NoError(err)

// send second part
req.Offset = int64(len(part1))
req.Data = part2
_, err = a.CopyFile(context.Background(), req)
assert.NoError(err)

// check file exist
assert.FileExists(req.Path)
content, err := ioutil.ReadFile(req.Path)
assert.NoError(err)
// check file's content
assert.Equal(content, append(part1, part2...))
}
Loading

0 comments on commit 8e48125

Please sign in to comment.