@@ -3,6 +3,7 @@ package sftp
3
3
import (
4
4
"io"
5
5
"os"
6
+ "path"
6
7
"regexp"
7
8
"sync"
8
9
"syscall"
@@ -278,3 +279,51 @@ func TestStatusFromError(t *testing.T) {
278
279
assert .Equal (t , tc .pkt , statusFromError (tc .pkt , tc .err ))
279
280
}
280
281
}
282
+
283
+ // This was written to test a race b/w open immediately followed by a stat.
284
+ // Previous to this the Open would trigger the use of a worker pool, then the
285
+ // stat packet would come in an hit the pool and return faster than the open
286
+ // (returning a file-not-found error).
287
+ // The below by itself wouldn't trigger the race however, I needed to add a
288
+ // small sleep in the openpacket code to trigger the issue. I wanted to add a
289
+ // way to inject that in the code but right now there is no good place for it.
290
+ // I'm thinking after I convert the server into a request-server backend I
291
+ // might be able to do something with the runWorker method passed into the
292
+ // packet manager. But with the 2 implementations fo the server it just doesn't
293
+ // fit well right now.
294
+ func TestOpenStatRace (t * testing.T ) {
295
+ client , server := clientServerPair (t )
296
+ defer client .Close ()
297
+ defer server .Close ()
298
+
299
+ // openpacket finishes to fast to trigger race in tests
300
+ // need to add a small sleep on server to openpackets somehow
301
+ tmppath := path .Join (os .TempDir (), "stat_race" )
302
+ pflags := flags (os .O_RDWR | os .O_CREATE | os .O_TRUNC )
303
+ ch := make (chan result , 3 )
304
+ id1 := client .nextID ()
305
+ client .dispatchRequest (ch , sshFxpOpenPacket {
306
+ ID : id1 ,
307
+ Path : tmppath ,
308
+ Pflags : pflags ,
309
+ })
310
+ id2 := client .nextID ()
311
+ client .dispatchRequest (ch , sshFxpLstatPacket {
312
+ ID : id2 ,
313
+ Path : tmppath ,
314
+ })
315
+ testreply := func (id uint32 , ch chan result ) {
316
+ r := <- ch
317
+ switch r .typ {
318
+ case ssh_FXP_ATTRS , ssh_FXP_HANDLE : // ignore
319
+ case ssh_FXP_STATUS :
320
+ err := normaliseError (unmarshalStatus (id , r .data ))
321
+ assert .NoError (t , err , "race hit, stat before open" )
322
+ default :
323
+ assert .Fail (t , "Unexpected type" )
324
+ }
325
+ }
326
+ testreply (id1 , ch )
327
+ testreply (id2 , ch )
328
+ os .Remove (tmppath )
329
+ }
0 commit comments