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 14package sftpfs 15 16import ( 17 "os" 18 "time" 19 20 "github.com/pkg/sftp" 21 "github.com/spf13/afero" 22) 23 24// Fs is a afero.Fs implementation that uses functions provided by the sftp package. 25// 26// For details in any method, check the documentation of the sftp package 27// (github.com/pkg/sftp). 28type Fs struct { 29 client *sftp.Client 30} 31 32func New(client *sftp.Client) afero.Fs { 33 return &Fs{client: client} 34} 35 36func (s Fs) Name() string { return "sftpfs" } 37 38func (s Fs) Create(name string) (afero.File, error) { 39 return FileCreate(s.client, name) 40} 41 42func (s Fs) Mkdir(name string, perm os.FileMode) error { 43 err := s.client.Mkdir(name) 44 if err != nil { 45 return err 46 } 47 return s.client.Chmod(name, perm) 48} 49 50func (s Fs) MkdirAll(path string, perm os.FileMode) error { 51 // Fast path: if we can tell whether path is a directory or file, stop with success or error. 52 dir, err := s.Stat(path) 53 if err == nil { 54 if dir.IsDir() { 55 return nil 56 } 57 return err 58 } 59 60 // Slow path: make sure parent exists and then call Mkdir for path. 61 i := len(path) 62 for i > 0 && os.IsPathSeparator(path[i-1]) { // Skip trailing path separator. 63 i-- 64 } 65 66 j := i 67 for j > 0 && !os.IsPathSeparator(path[j-1]) { // Scan backward over element. 68 j-- 69 } 70 71 if j > 1 { 72 // Create parent 73 err = s.MkdirAll(path[0:j-1], perm) 74 if err != nil { 75 return err 76 } 77 } 78 79 // Parent now exists; invoke Mkdir and use its result. 80 err = s.Mkdir(path, perm) 81 if err != nil { 82 // Handle arguments like "foo/." by 83 // double-checking that directory doesn't exist. 84 dir, err1 := s.Lstat(path) 85 if err1 == nil && dir.IsDir() { 86 return nil 87 } 88 return err 89 } 90 return nil 91} 92 93func (s Fs) Open(name string) (afero.File, error) { 94 return FileOpen(s.client, name) 95} 96 97func (s Fs) OpenFile(name string, flag int, perm os.FileMode) (afero.File, error) { 98 return nil, nil 99} 100 101func (s Fs) Remove(name string) error { 102 return s.client.Remove(name) 103} 104 105func (s Fs) RemoveAll(path string) error { 106 // TODO have a look at os.RemoveAll 107 // https://github.com/golang/go/blob/master/src/os/path.go#L66 108 return nil 109} 110 111func (s Fs) Rename(oldname, newname string) error { 112 return s.client.Rename(oldname, newname) 113} 114 115func (s Fs) Stat(name string) (os.FileInfo, error) { 116 return s.client.Stat(name) 117} 118 119func (s Fs) Lstat(p string) (os.FileInfo, error) { 120 return s.client.Lstat(p) 121} 122 123func (s Fs) Chmod(name string, mode os.FileMode) error { 124 return s.client.Chmod(name, mode) 125} 126 127func (s Fs) Chtimes(name string, atime time.Time, mtime time.Time) error { 128 return s.client.Chtimes(name, atime, mtime) 129} 130