@@ -17,6 +17,8 @@ import (
17
17
18
18
"github.com/kr/fs"
19
19
"golang.org/x/crypto/ssh"
20
+
21
+ "github.com/pkg/sftp/internal/encoding/ssh/filexfer/openssh"
20
22
)
21
23
22
24
var (
@@ -758,20 +760,39 @@ func (c *Client) Join(elem ...string) string { return path.Join(elem...) }
758
760
// file or directory with the specified path exists, or if the specified directory
759
761
// is not empty.
760
762
func (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
769
783
}
770
784
}
771
- if os .IsPermission (err ) {
772
- return c .RemoveDirectory (path )
785
+
786
+ fi , err := c .Stat (path )
787
+ if err != nil {
788
+ return err
773
789
}
774
- return err
790
+
791
+ if fi .IsDir () {
792
+ return errD
793
+ }
794
+
795
+ return errF
775
796
}
776
797
777
798
func (c * Client ) removeFile (path string ) error {
@@ -785,7 +806,15 @@ func (c *Client) removeFile(path string) error {
785
806
}
786
807
switch typ {
787
808
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
+ }
789
818
default :
790
819
return unimplementedPacketErr (typ )
791
820
}
@@ -803,7 +832,15 @@ func (c *Client) RemoveDirectory(path string) error {
803
832
}
804
833
switch typ {
805
834
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
+ }
807
844
default :
808
845
return unimplementedPacketErr (typ )
809
846
}
@@ -2122,6 +2159,13 @@ func (f *File) Sync() error {
2122
2159
return os .ErrClosed
2123
2160
}
2124
2161
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
+
2125
2169
id := f .c .nextID ()
2126
2170
typ , data , err := f .c .sendPacket (context .Background (), nil , & sshFxpFsyncPacket {
2127
2171
ID : id ,
0 commit comments