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 }