github.com/pkg/sftp@v1.13.6/sftp.go (about)

     1  // Package sftp implements the SSH File Transfer Protocol as described in
     2  // https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-02.txt
     3  package sftp
     4  
     5  import (
     6  	"fmt"
     7  )
     8  
     9  const (
    10  	sshFxpInit          = 1
    11  	sshFxpVersion       = 2
    12  	sshFxpOpen          = 3
    13  	sshFxpClose         = 4
    14  	sshFxpRead          = 5
    15  	sshFxpWrite         = 6
    16  	sshFxpLstat         = 7
    17  	sshFxpFstat         = 8
    18  	sshFxpSetstat       = 9
    19  	sshFxpFsetstat      = 10
    20  	sshFxpOpendir       = 11
    21  	sshFxpReaddir       = 12
    22  	sshFxpRemove        = 13
    23  	sshFxpMkdir         = 14
    24  	sshFxpRmdir         = 15
    25  	sshFxpRealpath      = 16
    26  	sshFxpStat          = 17
    27  	sshFxpRename        = 18
    28  	sshFxpReadlink      = 19
    29  	sshFxpSymlink       = 20
    30  	sshFxpStatus        = 101
    31  	sshFxpHandle        = 102
    32  	sshFxpData          = 103
    33  	sshFxpName          = 104
    34  	sshFxpAttrs         = 105
    35  	sshFxpExtended      = 200
    36  	sshFxpExtendedReply = 201
    37  )
    38  
    39  const (
    40  	sshFxOk               = 0
    41  	sshFxEOF              = 1
    42  	sshFxNoSuchFile       = 2
    43  	sshFxPermissionDenied = 3
    44  	sshFxFailure          = 4
    45  	sshFxBadMessage       = 5
    46  	sshFxNoConnection     = 6
    47  	sshFxConnectionLost   = 7
    48  	sshFxOPUnsupported    = 8
    49  
    50  	// see draft-ietf-secsh-filexfer-13
    51  	// https://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-9.1
    52  	sshFxInvalidHandle           = 9
    53  	sshFxNoSuchPath              = 10
    54  	sshFxFileAlreadyExists       = 11
    55  	sshFxWriteProtect            = 12
    56  	sshFxNoMedia                 = 13
    57  	sshFxNoSpaceOnFilesystem     = 14
    58  	sshFxQuotaExceeded           = 15
    59  	sshFxUnknownPrincipal        = 16
    60  	sshFxLockConflict            = 17
    61  	sshFxDirNotEmpty             = 18
    62  	sshFxNotADirectory           = 19
    63  	sshFxInvalidFilename         = 20
    64  	sshFxLinkLoop                = 21
    65  	sshFxCannotDelete            = 22
    66  	sshFxInvalidParameter        = 23
    67  	sshFxFileIsADirectory        = 24
    68  	sshFxByteRangeLockConflict   = 25
    69  	sshFxByteRangeLockRefused    = 26
    70  	sshFxDeletePending           = 27
    71  	sshFxFileCorrupt             = 28
    72  	sshFxOwnerInvalid            = 29
    73  	sshFxGroupInvalid            = 30
    74  	sshFxNoMatchingByteRangeLock = 31
    75  )
    76  
    77  const (
    78  	sshFxfRead   = 0x00000001
    79  	sshFxfWrite  = 0x00000002
    80  	sshFxfAppend = 0x00000004
    81  	sshFxfCreat  = 0x00000008
    82  	sshFxfTrunc  = 0x00000010
    83  	sshFxfExcl   = 0x00000020
    84  )
    85  
    86  var (
    87  	// supportedSFTPExtensions defines the supported extensions
    88  	supportedSFTPExtensions = []sshExtensionPair{
    89  		{"hardlink@openssh.com", "1"},
    90  		{"posix-rename@openssh.com", "1"},
    91  		{"statvfs@openssh.com", "2"},
    92  	}
    93  	sftpExtensions = supportedSFTPExtensions
    94  )
    95  
    96  type fxp uint8
    97  
    98  func (f fxp) String() string {
    99  	switch f {
   100  	case sshFxpInit:
   101  		return "SSH_FXP_INIT"
   102  	case sshFxpVersion:
   103  		return "SSH_FXP_VERSION"
   104  	case sshFxpOpen:
   105  		return "SSH_FXP_OPEN"
   106  	case sshFxpClose:
   107  		return "SSH_FXP_CLOSE"
   108  	case sshFxpRead:
   109  		return "SSH_FXP_READ"
   110  	case sshFxpWrite:
   111  		return "SSH_FXP_WRITE"
   112  	case sshFxpLstat:
   113  		return "SSH_FXP_LSTAT"
   114  	case sshFxpFstat:
   115  		return "SSH_FXP_FSTAT"
   116  	case sshFxpSetstat:
   117  		return "SSH_FXP_SETSTAT"
   118  	case sshFxpFsetstat:
   119  		return "SSH_FXP_FSETSTAT"
   120  	case sshFxpOpendir:
   121  		return "SSH_FXP_OPENDIR"
   122  	case sshFxpReaddir:
   123  		return "SSH_FXP_READDIR"
   124  	case sshFxpRemove:
   125  		return "SSH_FXP_REMOVE"
   126  	case sshFxpMkdir:
   127  		return "SSH_FXP_MKDIR"
   128  	case sshFxpRmdir:
   129  		return "SSH_FXP_RMDIR"
   130  	case sshFxpRealpath:
   131  		return "SSH_FXP_REALPATH"
   132  	case sshFxpStat:
   133  		return "SSH_FXP_STAT"
   134  	case sshFxpRename:
   135  		return "SSH_FXP_RENAME"
   136  	case sshFxpReadlink:
   137  		return "SSH_FXP_READLINK"
   138  	case sshFxpSymlink:
   139  		return "SSH_FXP_SYMLINK"
   140  	case sshFxpStatus:
   141  		return "SSH_FXP_STATUS"
   142  	case sshFxpHandle:
   143  		return "SSH_FXP_HANDLE"
   144  	case sshFxpData:
   145  		return "SSH_FXP_DATA"
   146  	case sshFxpName:
   147  		return "SSH_FXP_NAME"
   148  	case sshFxpAttrs:
   149  		return "SSH_FXP_ATTRS"
   150  	case sshFxpExtended:
   151  		return "SSH_FXP_EXTENDED"
   152  	case sshFxpExtendedReply:
   153  		return "SSH_FXP_EXTENDED_REPLY"
   154  	default:
   155  		return "unknown"
   156  	}
   157  }
   158  
   159  type fx uint8
   160  
   161  func (f fx) String() string {
   162  	switch f {
   163  	case sshFxOk:
   164  		return "SSH_FX_OK"
   165  	case sshFxEOF:
   166  		return "SSH_FX_EOF"
   167  	case sshFxNoSuchFile:
   168  		return "SSH_FX_NO_SUCH_FILE"
   169  	case sshFxPermissionDenied:
   170  		return "SSH_FX_PERMISSION_DENIED"
   171  	case sshFxFailure:
   172  		return "SSH_FX_FAILURE"
   173  	case sshFxBadMessage:
   174  		return "SSH_FX_BAD_MESSAGE"
   175  	case sshFxNoConnection:
   176  		return "SSH_FX_NO_CONNECTION"
   177  	case sshFxConnectionLost:
   178  		return "SSH_FX_CONNECTION_LOST"
   179  	case sshFxOPUnsupported:
   180  		return "SSH_FX_OP_UNSUPPORTED"
   181  	default:
   182  		return "unknown"
   183  	}
   184  }
   185  
   186  type unexpectedPacketErr struct {
   187  	want, got uint8
   188  }
   189  
   190  func (u *unexpectedPacketErr) Error() string {
   191  	return fmt.Sprintf("sftp: unexpected packet: want %v, got %v", fxp(u.want), fxp(u.got))
   192  }
   193  
   194  func unimplementedPacketErr(u uint8) error {
   195  	return fmt.Errorf("sftp: unimplemented packet type: got %v", fxp(u))
   196  }
   197  
   198  type unexpectedIDErr struct{ want, got uint32 }
   199  
   200  func (u *unexpectedIDErr) Error() string {
   201  	return fmt.Sprintf("sftp: unexpected id: want %d, got %d", u.want, u.got)
   202  }
   203  
   204  func unimplementedSeekWhence(whence int) error {
   205  	return fmt.Errorf("sftp: unimplemented seek whence %d", whence)
   206  }
   207  
   208  func unexpectedCount(want, got uint32) error {
   209  	return fmt.Errorf("sftp: unexpected count: want %d, got %d", want, got)
   210  }
   211  
   212  type unexpectedVersionErr struct{ want, got uint32 }
   213  
   214  func (u *unexpectedVersionErr) Error() string {
   215  	return fmt.Sprintf("sftp: unexpected server version: want %v, got %v", u.want, u.got)
   216  }
   217  
   218  // A StatusError is returned when an SFTP operation fails, and provides
   219  // additional information about the failure.
   220  type StatusError struct {
   221  	Code      uint32
   222  	msg, lang string
   223  }
   224  
   225  func (s *StatusError) Error() string {
   226  	return fmt.Sprintf("sftp: %q (%v)", s.msg, fx(s.Code))
   227  }
   228  
   229  // FxCode returns the error code typed to match against the exported codes
   230  func (s *StatusError) FxCode() fxerr {
   231  	return fxerr(s.Code)
   232  }
   233  
   234  func getSupportedExtensionByName(extensionName string) (sshExtensionPair, error) {
   235  	for _, supportedExtension := range supportedSFTPExtensions {
   236  		if supportedExtension.Name == extensionName {
   237  			return supportedExtension, nil
   238  		}
   239  	}
   240  	return sshExtensionPair{}, fmt.Errorf("unsupported extension: %s", extensionName)
   241  }
   242  
   243  // SetSFTPExtensions allows to customize the supported server extensions.
   244  // See the variable supportedSFTPExtensions for supported extensions.
   245  // This method accepts a slice of sshExtensionPair names for example 'hardlink@openssh.com'.
   246  // If an invalid extension is given an error will be returned and nothing will be changed
   247  func SetSFTPExtensions(extensions ...string) error {
   248  	tempExtensions := []sshExtensionPair{}
   249  	for _, extension := range extensions {
   250  		sftpExtension, err := getSupportedExtensionByName(extension)
   251  		if err != nil {
   252  			return err
   253  		}
   254  		tempExtensions = append(tempExtensions, sftpExtension)
   255  	}
   256  	sftpExtensions = tempExtensions
   257  	return nil
   258  }