@@ -17,6 +17,8 @@ import (
1717
1818 "github.com/kr/fs"
1919 "golang.org/x/crypto/ssh"
20+
21+ "github.com/pkg/sftp/internal/encoding/ssh/filexfer/openssh"
2022)
2123
2224var (
@@ -758,20 +760,39 @@ func (c *Client) Join(elem ...string) string { return path.Join(elem...) }
758760// file or directory with the specified path exists, or if the specified directory
759761// is not empty.
760762func (c * Client ) Remove (path string ) error {
761- err := c .removeFile (path )
762- // some servers, *cough* osx *cough*, return EPERM, not ENODIR.
763- // serv-u returns ssh_FX_FILE_IS_A_DIRECTORY
764- // EPERM is converted to os.ErrPermission so it is not a StatusError
765- if err , ok := err .(* StatusError ); ok {
766- switch err .Code {
767- case sshFxFailure , sshFxFileIsADirectory :
768- return c .RemoveDirectory (path )
763+ errF := c .removeFile (path )
764+ if errF == nil {
765+ return nil
766+ }
767+
768+ errD := c .RemoveDirectory (path )
769+ if errD == nil {
770+ return nil
771+ }
772+
773+ // Both failed: figure out which error to return.
774+
775+ if errF , ok := errF .(* os.PathError ); ok {
776+ // The only time it makes sense to compare errors, is when both are `*os.PathError`.
777+ // We cannot test these directly with errF == errD, as that would be a pointer comparison.
778+
779+ if errD , ok := errD .(* os.PathError ); ok && errors .Is (errF .Err , errD .Err ) {
780+ // If they are both pointers to PathError,
781+ // and the same underlying error, then return that.
782+ return errF
769783 }
770784 }
771- if os .IsPermission (err ) {
772- return c .RemoveDirectory (path )
785+
786+ fi , err := c .Stat (path )
787+ if err != nil {
788+ return err
773789 }
774- return err
790+
791+ if fi .IsDir () {
792+ return errD
793+ }
794+
795+ return errF
775796}
776797
777798func (c * Client ) removeFile (path string ) error {
@@ -785,7 +806,15 @@ func (c *Client) removeFile(path string) error {
785806 }
786807 switch typ {
787808 case sshFxpStatus :
788- return normaliseError (unmarshalStatus (id , data ))
809+ err = normaliseError (unmarshalStatus (id , data ))
810+ if err == nil {
811+ return nil
812+ }
813+ return & os.PathError {
814+ Op : "remove" ,
815+ Path : path ,
816+ Err : err ,
817+ }
789818 default :
790819 return unimplementedPacketErr (typ )
791820 }
@@ -803,7 +832,15 @@ func (c *Client) RemoveDirectory(path string) error {
803832 }
804833 switch typ {
805834 case sshFxpStatus :
806- return normaliseError (unmarshalStatus (id , data ))
835+ err = normaliseError (unmarshalStatus (id , data ))
836+ if err == nil {
837+ return nil
838+ }
839+ return & os.PathError {
840+ Op : "remove" ,
841+ Path : path ,
842+ Err : err ,
843+ }
807844 default :
808845 return unimplementedPacketErr (typ )
809846 }
@@ -2122,6 +2159,13 @@ func (f *File) Sync() error {
21222159 return os .ErrClosed
21232160 }
21242161
2162+ if data , ok := f .c .HasExtension (openssh .ExtensionFSync ().Name ); ! ok || data != "1" {
2163+ return & StatusError {
2164+ Code : sshFxOPUnsupported ,
2165+ msg : "fsync not supported" ,
2166+ }
2167+ }
2168+
21252169 id := f .c .nextID ()
21262170 typ , data , err := f .c .sendPacket (context .Background (), nil , & sshFxpFsyncPacket {
21272171 ID : id ,
0 commit comments