@@ -98,6 +98,11 @@ func (c *HostConnectedEndpoint) init() *syserr.Error {
98
98
}
99
99
100
100
func (c * HostConnectedEndpoint ) initFromOptions () * syserr.Error {
101
+ if c .fd < 0 {
102
+ // There is no underlying FD to restore; nothing to do
103
+ return nil
104
+ }
105
+
101
106
family , err := unix .GetsockoptInt (c .fd , unix .SOL_SOCKET , unix .SO_DOMAIN )
102
107
if err != nil {
103
108
return syserr .FromError (err )
@@ -163,6 +168,10 @@ func (c *HostConnectedEndpoint) Send(ctx context.Context, data [][]byte, control
163
168
return 0 , false , syserr .ErrInvalidEndpointState
164
169
}
165
170
171
+ if c .IsSendClosed () {
172
+ return 0 , false , syserr .ErrClosedForSend
173
+ }
174
+
166
175
// Since stream sockets don't preserve message boundaries, we can write
167
176
// only as much of the message as fits in the send buffer.
168
177
truncate := c .stype == linux .SOCK_STREAM
@@ -192,6 +201,14 @@ func (c *HostConnectedEndpoint) SendNotify() {}
192
201
func (c * HostConnectedEndpoint ) CloseSend () {
193
202
c .mu .Lock ()
194
203
defer c .mu .Unlock ()
204
+ c .closeSendLocked ()
205
+ }
206
+
207
+ // Preconditions: c.mu must be held.
208
+ func (c * HostConnectedEndpoint ) closeSendLocked () {
209
+ if c .IsSendClosed () {
210
+ return
211
+ }
195
212
196
213
if err := unix .Shutdown (c .fd , unix .SHUT_WR ); err != nil {
197
214
// A well-formed UDS shutdown can't fail. See
@@ -300,6 +317,14 @@ func (c *HostConnectedEndpoint) RecvNotify() {}
300
317
func (c * HostConnectedEndpoint ) CloseRecv () {
301
318
c .mu .Lock ()
302
319
defer c .mu .Unlock ()
320
+ c .closeRecvLocked ()
321
+ }
322
+
323
+ // Preconditions: c.mu must be held.
324
+ func (c * HostConnectedEndpoint ) closeRecvLocked () {
325
+ if c .IsRecvClosed () {
326
+ return
327
+ }
303
328
304
329
if err := unix .Shutdown (c .fd , unix .SHUT_RD ); err != nil {
305
330
// A well-formed UDS shutdown can't fail. See
@@ -382,13 +407,34 @@ func (c *HostConnectedEndpoint) SetReceiveBufferSize(v int64) (newSz int64) {
382
407
// SCMConnectedEndpoint represents an endpoint backed by a host fd that was
383
408
// passed through a gofer Unix socket. It resembles HostConnectedEndpoint, with the
384
409
// following differences:
385
- // - SCMConnectedEndpoint is not saveable, because the host cannot guarantee
386
- // the same descriptor number across S/R.
410
+ // - SCMConnectedEndpoint is not saveable by default, because the host
411
+ // cannot guarantee the same descriptor number across S/R.
412
+ // However, it can optionally be placed in a closed state before save.
387
413
// - SCMConnectedEndpoint holds ownership of its fd and notification queue.
414
+ //
415
+ // +stateify savable
388
416
type SCMConnectedEndpoint struct {
389
417
HostConnectedEndpoint
390
418
391
419
queue * waiter.Queue
420
+ opts UnixSocketOpts
421
+ }
422
+
423
+ // beforeSave is invoked by stateify.
424
+ func (e * SCMConnectedEndpoint ) beforeSave () {
425
+ if ! e .opts .DisconnectOnSave {
426
+ panic ("socket cannot be saved in a connected state" )
427
+ }
428
+
429
+ e .mu .Lock ()
430
+ defer e .mu .Unlock ()
431
+ fdnotifier .RemoveFD (int32 (e .fd ))
432
+ e .closeRecvLocked ()
433
+ e .closeSendLocked ()
434
+ if err := unix .Close (e .fd ); err != nil {
435
+ log .Warningf ("Failed to close host fd %d: %v" , err )
436
+ }
437
+ e .destroyLocked ()
392
438
}
393
439
394
440
// Init will do the initialization required without holding other locks.
@@ -400,12 +446,17 @@ func (e *SCMConnectedEndpoint) Init() error {
400
446
func (e * SCMConnectedEndpoint ) Release (ctx context.Context ) {
401
447
e .DecRef (func () {
402
448
e .mu .Lock ()
449
+ defer e .mu .Unlock ()
450
+
451
+ if e .fd < 0 {
452
+ return
453
+ }
454
+
403
455
fdnotifier .RemoveFD (int32 (e .fd ))
404
456
if err := unix .Close (e .fd ); err != nil {
405
457
log .Warningf ("Failed to close host fd %d: %v" , err )
406
458
}
407
459
e .destroyLocked ()
408
- e .mu .Unlock ()
409
460
})
410
461
}
411
462
@@ -415,13 +466,14 @@ func (e *SCMConnectedEndpoint) Release(ctx context.Context) {
415
466
// The caller is responsible for calling Init(). Additionally, Release needs to
416
467
// be called twice because ConnectedEndpoint is both a Receiver and
417
468
// ConnectedEndpoint.
418
- func NewSCMEndpoint (hostFD int , queue * waiter.Queue , addr string ) (* SCMConnectedEndpoint , * syserr.Error ) {
469
+ func NewSCMEndpoint (hostFD int , queue * waiter.Queue , addr string , opts UnixSocketOpts ) (* SCMConnectedEndpoint , * syserr.Error ) {
419
470
e := SCMConnectedEndpoint {
420
471
HostConnectedEndpoint : HostConnectedEndpoint {
421
472
fd : hostFD ,
422
473
addr : addr ,
423
474
},
424
475
queue : queue ,
476
+ opts : opts ,
425
477
}
426
478
427
479
if err := e .init (); err != nil {
0 commit comments