go.mondoo.com/cnquery@v0.0.0-20231005093811-59568235f6ea/providers/os/connection/docker_file.go (about)

     1  // Copyright (c) Mondoo, Inc.
     2  // SPDX-License-Identifier: BUSL-1.1
     3  
     4  package connection
     5  
     6  import (
     7  	"bytes"
     8  	"context"
     9  	"errors"
    10  	"fmt"
    11  	"io"
    12  	"os"
    13  	"path/filepath"
    14  	"strings"
    15  
    16  	dockertypes "github.com/docker/docker/api/types"
    17  	"github.com/docker/docker/client"
    18  	"github.com/spf13/afero"
    19  	"go.mondoo.com/cnquery/providers/os/connection/ssh/cat"
    20  	"go.mondoo.com/cnquery/providers/os/fsutil"
    21  )
    22  
    23  func FileOpen(dockerClient *client.Client, path string, container string, conn *DockerContainerConnection, catFs *cat.Fs) (afero.File, error) {
    24  	f := &File{
    25  		path:         path,
    26  		dockerClient: dockerClient,
    27  		container:    container,
    28  		connection:   conn,
    29  		catFs:        catFs,
    30  	}
    31  	err := f.Open()
    32  	return f, err
    33  }
    34  
    35  type File struct {
    36  	path         string
    37  	container    string
    38  	dockerClient *client.Client
    39  	connection   *DockerContainerConnection
    40  	reader       *bytes.Reader
    41  	catFs        *cat.Fs
    42  }
    43  
    44  func (f *File) Open() error {
    45  	r, _, err := f.getFileDockerReader(f.path)
    46  	if err != nil {
    47  		return os.ErrNotExist
    48  	}
    49  	defer r.Close()
    50  	data, err := fsutil.ReadFileFromTarStream(r)
    51  	if err != nil {
    52  		return err
    53  	}
    54  	f.reader = bytes.NewReader(data)
    55  	return nil
    56  }
    57  
    58  func (f *File) Close() error {
    59  	return nil
    60  }
    61  
    62  func (f *File) Name() string {
    63  	return f.path
    64  }
    65  
    66  func (f *File) Stat() (os.FileInfo, error) {
    67  	return f.catFs.Stat(f.path)
    68  }
    69  
    70  func (f *File) Sync() error {
    71  	return errors.New("not implemented")
    72  }
    73  
    74  func (f *File) Truncate(size int64) error {
    75  	return errors.New("not implemented")
    76  }
    77  
    78  func (f *File) Read(b []byte) (n int, err error) {
    79  	return f.reader.Read(b)
    80  }
    81  
    82  func (f *File) ReadAt(b []byte, off int64) (n int, err error) {
    83  	return f.reader.ReadAt(b, off)
    84  }
    85  
    86  func (f *File) Readdir(count int) (res []os.FileInfo, err error) {
    87  	return nil, errors.New("not implemented")
    88  }
    89  
    90  func (f *File) Readdirnames(n int) ([]string, error) {
    91  	c, err := f.connection.RunCommand(fmt.Sprintf("find %s -maxdepth 1 -type d", f.path))
    92  	if err != nil {
    93  		return []string{}, err
    94  	}
    95  
    96  	content, err := io.ReadAll(c.Stdout)
    97  	if err != nil {
    98  		return []string{}, err
    99  	}
   100  
   101  	directories := strings.Split(string(content), "\n")
   102  
   103  	// first result is always self
   104  	if len(directories) > 0 {
   105  		directories = directories[1:]
   106  	}
   107  
   108  	// extract names
   109  	basenames := make([]string, len(directories))
   110  	for i := range directories {
   111  		basenames[i] = filepath.Base(directories[i])
   112  	}
   113  	return basenames, nil
   114  }
   115  
   116  func (f *File) Seek(offset int64, whence int) (int64, error) {
   117  	return 0, errors.New("not implemented")
   118  }
   119  
   120  func (f *File) Write(b []byte) (n int, err error) {
   121  	return 0, errors.New("not implemented")
   122  }
   123  
   124  func (f *File) WriteAt(b []byte, off int64) (n int, err error) {
   125  	return 0, errors.New("not implemented")
   126  }
   127  
   128  func (f *File) WriteString(s string) (ret int, err error) {
   129  	return 0, errors.New("not implemented")
   130  }
   131  
   132  func (f *File) getFileDockerReader(path string) (io.ReadCloser, dockertypes.ContainerPathStat, error) {
   133  	r, stat, err := f.dockerClient.CopyFromContainer(context.Background(), f.container, path)
   134  
   135  	// follow symlink if stat.LinkTarget is set
   136  	if len(stat.LinkTarget) > 0 {
   137  		return f.getFileDockerReader(stat.LinkTarget)
   138  	}
   139  
   140  	return r, stat, err
   141  }
   142  
   143  // returns a TarReader stream the caller is responsible for closing the stream
   144  func (f *File) Tar() (io.ReadCloser, error) {
   145  	r, _, err := f.getFileDockerReader(f.path)
   146  	return r, err
   147  }
   148  
   149  // func (f *File) Exists() bool {
   150  // 	if strings.HasPrefix(f.path, "/proc") {
   151  // 		entries := f.procls()
   152  // 		for i := range entries {
   153  // 			if entries[i] == f.path {
   154  // 				return true
   155  // 			}
   156  // 		}
   157  // 		return false
   158  // 	}
   159  
   160  // 	r, _, err := f.getFileReader(f.path)
   161  // 	if err != nil {
   162  // 		return false
   163  // 	}
   164  // 	r.Close()
   165  // 	return true
   166  // }
   167  
   168  // returns all directories and files under /proc
   169  func (f *File) procls() []string {
   170  	c, err := f.connection.RunCommand("find /proc")
   171  	if err != nil {
   172  		return []string{}
   173  	}
   174  	content, err := io.ReadAll(c.Stdout)
   175  	if err != nil {
   176  		return []string{}
   177  	}
   178  
   179  	// all files
   180  	return strings.Split(string(content), "\n")
   181  }