go.mondoo.com/cnquery@v0.0.0-20231005093811-59568235f6ea/providers/os/connection/ssh/sftp/sftp.go (about) 1 // Copyright © 2015 Jerry Jacobs <jerry.jacobs@xor-gate.org>. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // http://www.apache.org/licenses/LICENSE-2.0 7 // 8 // Unless required by applicable law or agreed to in writing, software 9 // distributed under the License is distributed on an "AS IS" BASIS, 10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package sftp 15 16 import ( 17 "os" 18 "strings" 19 "time" 20 21 "github.com/cockroachdb/errors" 22 "github.com/pkg/sftp" 23 "github.com/spf13/afero" 24 "go.mondoo.com/cnquery/providers/os/connection/ssh/cat" 25 "golang.org/x/crypto/ssh" 26 ) 27 28 // Fs is a afero.Fs implementation that uses functions provided by the sftp package. 29 // 30 // For details in any method, check the documentation of the sftp package 31 // (github.com/pkg/sftp). 32 type Fs struct { 33 client *sftp.Client 34 catFs *cat.Fs 35 } 36 37 func New(commandRunner cat.CommandRunner, client *ssh.Client) (afero.Fs, error) { 38 ftpClient, err := sftpClient(client) 39 if err != nil { 40 return nil, errors.Wrap(err, "could not initialize sftp backend") 41 } 42 43 return &Fs{ 44 client: ftpClient, 45 catFs: cat.New(commandRunner), 46 }, nil 47 } 48 49 func sftpClient(sshClient *ssh.Client) (*sftp.Client, error) { 50 c, err := sftp.NewClient(sshClient, sftp.MaxPacket(1<<15)) 51 if err != nil { 52 return nil, err 53 } 54 return c, nil 55 } 56 57 func (s Fs) Name() string { return "sftpfs" } 58 59 func (s Fs) Create(name string) (afero.File, error) { 60 return FileCreate(s.client, name) 61 } 62 63 func (s Fs) Mkdir(name string, perm os.FileMode) error { 64 err := s.client.Mkdir(name) 65 if err != nil { 66 return err 67 } 68 return s.client.Chmod(name, perm) 69 } 70 71 func (s Fs) MkdirAll(path string, perm os.FileMode) error { 72 // Fast path: if we can tell whether path is a directory or file, stop with success or error. 73 dir, err := s.Stat(path) 74 if err == nil { 75 if dir.IsDir() { 76 return nil 77 } 78 return err 79 } 80 81 // Slow path: make sure parent exists and then call Mkdir for path. 82 i := len(path) 83 for i > 0 && os.IsPathSeparator(path[i-1]) { // Skip trailing path separator. 84 i-- 85 } 86 87 j := i 88 for j > 0 && !os.IsPathSeparator(path[j-1]) { // Scan backward over element. 89 j-- 90 } 91 92 if j > 1 { 93 // Create parent 94 err = s.MkdirAll(path[0:j-1], perm) 95 if err != nil { 96 return err 97 } 98 } 99 100 // Parent now exists; invoke Mkdir and use its result. 101 err = s.Mkdir(path, perm) 102 if err != nil { 103 // Handle arguments like "foo/." by 104 // double-checking that directory doesn't exist. 105 dir, err1 := s.Lstat(path) 106 if err1 == nil && dir.IsDir() { 107 return nil 108 } 109 return err 110 } 111 return nil 112 } 113 114 func (s Fs) Open(path string) (afero.File, error) { 115 // NOTE: procfs cannot be read via scp, so we fall-back to catfs all paths there 116 if strings.HasPrefix(path, "/proc") { 117 return s.catFs.Open(path) 118 } 119 120 return FileOpen(s.client, path) 121 } 122 123 func (s Fs) OpenFile(name string, flag int, perm os.FileMode) (afero.File, error) { 124 // sftp client does not support mode 125 sshfsFile, err := s.client.OpenFile(name, flag) 126 if err != nil { 127 return nil, err 128 } 129 return &File{fd: sshfsFile}, nil 130 } 131 132 func (s Fs) Remove(name string) error { 133 return s.client.Remove(name) 134 } 135 136 func (s Fs) RemoveAll(path string) error { 137 // TODO have a look at os.RemoveAll 138 // https://github.com/golang/go/blob/master/src/os/path.go#L66 139 return errors.New("removeall not implemented") 140 } 141 142 func (s Fs) Rename(oldname, newname string) error { 143 return s.client.Rename(oldname, newname) 144 } 145 146 func (s Fs) Stat(name string) (os.FileInfo, error) { 147 return s.client.Stat(name) 148 } 149 150 func (s Fs) Lstat(p string) (os.FileInfo, error) { 151 return s.client.Lstat(p) 152 } 153 154 func (s Fs) Chmod(name string, mode os.FileMode) error { 155 return s.client.Chmod(name, mode) 156 } 157 158 func (s Fs) Chtimes(name string, atime time.Time, mtime time.Time) error { 159 return s.client.Chtimes(name, atime, mtime) 160 } 161 162 func (s Fs) Chown(name string, uid, gid int) error { 163 return s.client.Chown(name, uid, gid) 164 }