1
2
3
4
5 package poll
6
7 import (
8 "errors"
9 "internal/race"
10 "internal/syscall/windows"
11 "io"
12 "runtime"
13 "sync"
14 "syscall"
15 "unicode/utf16"
16 "unicode/utf8"
17 "unsafe"
18 )
19
20 var (
21 initErr error
22 ioSync uint64
23 )
24
25
26
27 var ifsHandlesOnly = sync.OnceValue(func() bool {
28 protos := [2]int32{syscall.IPPROTO_TCP, 0}
29 var buf [32]syscall.WSAProtocolInfo
30 len := uint32(unsafe.Sizeof(buf))
31 n, err := syscall.WSAEnumProtocols(&protos[0], &buf[0], &len)
32 if err != nil {
33 return false
34 }
35 for i := range n {
36 if buf[i].ServiceFlags1&syscall.XP1_IFS_HANDLES == 0 {
37 return false
38 }
39 }
40 return true
41 })
42
43
44
45 func canSkipCompletionPortOnSuccess(h syscall.Handle, isSocket bool) bool {
46 if !isSocket {
47
48 return true
49 }
50 if ifsHandlesOnly() {
51
52 return true
53 }
54 var info syscall.WSAProtocolInfo
55 size := int32(unsafe.Sizeof(info))
56 if syscall.Getsockopt(h, syscall.SOL_SOCKET, windows.SO_PROTOCOL_INFOW, (*byte)(unsafe.Pointer(&info)), &size) != nil {
57 return false
58 }
59 return info.ServiceFlags1&syscall.XP1_IFS_HANDLES != 0
60 }
61
62
63
64
65 var InitWSA = sync.OnceFunc(func() {
66 var d syscall.WSAData
67 e := syscall.WSAStartup(uint32(0x202), &d)
68 if e != nil {
69 initErr = e
70 }
71 })
72
73
74 type operation struct {
75
76
77 o syscall.Overlapped
78
79
80 runtimeCtx uintptr
81 mode int32
82 }
83
84 func (o *operation) setOffset(off int64) {
85 o.o.OffsetHigh = uint32(off >> 32)
86 o.o.Offset = uint32(off)
87 }
88
89 func (fd *FD) overlapped(o *operation) *syscall.Overlapped {
90 if fd.isBlocking {
91
92
93
94
95 return nil
96 }
97 return &o.o
98 }
99
100 func newWsaBuf(b []byte) *syscall.WSABuf {
101 return &syscall.WSABuf{Buf: unsafe.SliceData(b), Len: uint32(len(b))}
102 }
103
104 var wsaBufsPool = sync.Pool{
105 New: func() any {
106 buf := make([]syscall.WSABuf, 0, 16)
107 return &buf
108 },
109 }
110
111 func newWSABufs(buf *[][]byte) *[]syscall.WSABuf {
112 bufsPtr := wsaBufsPool.Get().(*[]syscall.WSABuf)
113 *bufsPtr = (*bufsPtr)[:0]
114 for _, b := range *buf {
115 if len(b) == 0 {
116 *bufsPtr = append(*bufsPtr, syscall.WSABuf{})
117 continue
118 }
119 for len(b) > maxRW {
120 *bufsPtr = append(*bufsPtr, syscall.WSABuf{Len: maxRW, Buf: &b[0]})
121 b = b[maxRW:]
122 }
123 if len(b) > 0 {
124 *bufsPtr = append(*bufsPtr, syscall.WSABuf{Len: uint32(len(b)), Buf: &b[0]})
125 }
126 }
127 return bufsPtr
128 }
129
130 func freeWSABufs(bufsPtr *[]syscall.WSABuf) {
131
132 bufs := *bufsPtr
133 for i := range bufs {
134 bufs[i].Buf = nil
135 }
136
137
138
139
140
141
142 if cap(*bufsPtr) > 128 {
143 *bufsPtr = nil
144 }
145 wsaBufsPool.Put(bufsPtr)
146 }
147
148
149 var wsaMsgPool = sync.Pool{
150 New: func() any {
151 return &windows.WSAMsg{
152 Buffers: &syscall.WSABuf{},
153 BufferCount: 1,
154 }
155 },
156 }
157
158
159
160 func newWSAMsg(p []byte, oob []byte, flags int, rsa *wsaRsa) *windows.WSAMsg {
161
162
163
164
165
166 msg := wsaMsgPool.Get().(*windows.WSAMsg)
167 msg.Buffers.Len = uint32(len(p))
168 msg.Buffers.Buf = unsafe.SliceData(p)
169 if len(oob) > 0 {
170 msg.Control = syscall.WSABuf{
171 Len: uint32(len(oob)),
172 Buf: unsafe.SliceData(oob),
173 }
174 }
175 msg.Flags = uint32(flags)
176 if rsa != nil {
177 msg.Name = &rsa.name
178 msg.Namelen = rsa.namelen
179 }
180 return msg
181 }
182
183 func freeWSAMsg(msg *windows.WSAMsg) {
184
185 msg.Name = nil
186 msg.Namelen = 0
187 msg.Buffers.Len = 0
188 msg.Buffers.Buf = nil
189 msg.Control.Len = 0
190 msg.Control.Buf = nil
191 wsaMsgPool.Put(msg)
192 }
193
194
195
196
197
198 type wsaRsa struct {
199 name syscall.RawSockaddrAny
200 namelen int32
201 }
202
203 var wsaRsaPool = sync.Pool{
204 New: func() any {
205 return new(wsaRsa)
206 },
207 }
208
209 func newWSARsa() *wsaRsa {
210 rsa := wsaRsaPool.Get().(*wsaRsa)
211 rsa.name = syscall.RawSockaddrAny{}
212 rsa.namelen = int32(unsafe.Sizeof(syscall.RawSockaddrAny{}))
213 return rsa
214 }
215
216 var operationPool = sync.Pool{
217 New: func() any {
218 return new(operation)
219 },
220 }
221
222
223
224 func (fd *FD) waitIO(o *operation) error {
225 if o.o.HEvent != 0 {
226
227
228 if fd.kind == kindPipe && fd.closing() {
229 if err := syscall.CancelIoEx(fd.Sysfd, &o.o); err != nil && err != syscall.ERROR_NOT_FOUND {
230
231 panic(err)
232 }
233 }
234
235
236
237 _, err := syscall.WaitForSingleObject(o.o.HEvent, syscall.INFINITE)
238 return err
239 }
240
241 err := fd.pd.wait(int(o.mode), fd.isFile)
242 switch err {
243 case nil:
244
245 case ErrNetClosing, ErrFileClosing, ErrDeadlineExceeded:
246
247
248
249 if err := syscall.CancelIoEx(fd.Sysfd, &o.o); err != nil && err != syscall.ERROR_NOT_FOUND {
250
251 panic(err)
252 }
253 fd.pd.waitCanceled(int(o.mode))
254 default:
255
256 panic("unexpected runtime.netpoll error: " + err.Error())
257 }
258 return err
259 }
260
261
262
263
264
265 func (fd *FD) execIO(
266 mode int,
267 submit func(o *operation) (uint32, error),
268 pinPtrs ...any,
269 ) (int, error) {
270 if err := fd.ensureInit(); err != nil {
271 return 0, err
272 }
273
274 err := fd.pd.prepare(mode, fd.isFile)
275 if err != nil {
276 return 0, err
277 }
278 o := operationPool.Get().(*operation)
279 defer operationPool.Put(o)
280 *o = operation{
281 runtimeCtx: fd.pd.runtimeCtx,
282 mode: int32(mode),
283 }
284 if !fd.isBlocking {
285 var pinner *runtime.Pinner
286 if mode == 'r' {
287 pinner = &fd.readPinner
288 } else {
289 pinner = &fd.writePinner
290 }
291 defer pinner.Unpin()
292
293 pinner.Pin(o)
294 for _, ptr := range pinPtrs {
295 pinner.Pin(ptr)
296 }
297
298 if !fd.associated {
299
300
301
302 h, err := windows.CreateEvent(nil, 0, 0, nil)
303 if err != nil {
304
305 panic(err)
306 }
307
308 o.o.HEvent = h | 1
309 defer syscall.CloseHandle(h)
310 }
311 }
312
313 qty, err := submit(o)
314 var waitErr error
315
316
317 waitOnSuccess := o.o.HEvent == 0 && fd.waitOnSuccess
318
319
320 if !fd.isBlocking && (err == syscall.ERROR_IO_PENDING || (err == nil && waitOnSuccess)) {
321
322
323 waitErr = fd.waitIO(o)
324 if fd.isFile {
325 err = windows.GetOverlappedResult(fd.Sysfd, &o.o, &qty, false)
326 } else {
327 var flags uint32
328 err = windows.WSAGetOverlappedResult(fd.Sysfd, &o.o, &qty, false, &flags)
329 }
330 }
331 switch err {
332 case syscall.ERROR_OPERATION_ABORTED:
333
334
335
336 if waitErr != nil {
337
338 err = waitErr
339 } else if fd.kind == kindPipe && fd.closing() {
340
341
342
343 err = errClosing(fd.isFile)
344 }
345 case windows.ERROR_IO_INCOMPLETE:
346
347 if waitErr != nil {
348
349 err = waitErr
350 }
351 }
352 return int(qty), err
353 }
354
355
356
357 type FD struct {
358
359 fdmu fdMutex
360
361
362 Sysfd syscall.Handle
363
364
365 pd pollDesc
366
367
368
369
370
371 lazyInit bool
372 initOnce sync.Once
373 initMu sync.Mutex
374 skipIOCP bool
375
376
377
378
379
380
381 offset int64
382
383
384 lastbits []byte
385 readuint16 []uint16
386 readbyte []byte
387 readbyteOffset int
388
389
390 csema uint32
391
392
393
394 waitOnSuccess bool
395
396
397
398 IsStream bool
399
400
401
402 ZeroReadIsEOF bool
403
404
405
406 KeepFileCompletionModes bool
407
408
409 isFile bool
410
411
412 kind fileKind
413
414
415 isBlocking bool
416
417
418 associated bool
419
420
421
422 readPinner runtime.Pinner
423 writePinner runtime.Pinner
424 }
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439 func (fd *FD) setOffset(off int64) {
440 fd.offset = off
441 }
442
443
444 func (fd *FD) addOffset(off int) {
445 fd.offset += int64(off)
446 }
447
448
449 type fileKind byte
450
451 const (
452 kindNet fileKind = iota
453 kindFile
454 kindConsole
455 kindPipe
456 )
457
458
459
460
461
462
463
464
465
466 func (fd *FD) Init(net string, overlapped *bool) error {
467 if initErr != nil {
468 return initErr
469 }
470
471 switch net {
472 case "file":
473 fd.kind = kindFile
474 case "console":
475 fd.kind = kindConsole
476 case "pipe":
477 fd.kind = kindPipe
478 default:
479
480 fd.kind = kindNet
481 }
482 fd.isFile = fd.kind != kindNet
483 fd.lazyInit = overlapped == nil
484 fd.isBlocking = overlapped == nil || !*overlapped
485
486 if fd.isBlocking {
487 return nil
488 }
489 return fd.initIOCP()
490 }
491
492
493
494 func (fd *FD) ensureInit() error {
495 if !fd.lazyInit {
496 return nil
497 }
498 fd.initOnce.Do(func() {
499 fd.initMu.Lock()
500 defer fd.initMu.Unlock()
501 if fd.closing() {
502 return
503 }
504
505
506
507
508 overlapped, _ := windows.IsNonblock(fd.Sysfd)
509 fd.isBlocking = !overlapped
510 if overlapped && !fd.skipIOCP && !fd.closing() {
511
512
513 _ = fd.initIOCP()
514 }
515 })
516 if fd.closing() {
517 return errClosing(fd.isFile)
518 }
519 return nil
520 }
521
522
523
524
525 func (fd *FD) initIOCP() error {
526 var modes uint32
527 if fd.KeepFileCompletionModes {
528
529
530 var err error
531 modes, err = fd.getFileCompletionModes()
532 if err != nil {
533
534
535
536
537 return nil
538 }
539 }
540
541
542
543
544 if err := fd.pd.init(fd); err != nil {
545 return err
546 }
547 fd.associated = true
548
549 if !fd.KeepFileCompletionModes {
550
551 modes = fd.setFileCompletionModes()
552 }
553 fd.waitOnSuccess = modes&syscall.FILE_SKIP_COMPLETION_PORT_ON_SUCCESS == 0
554 return nil
555 }
556
557
558 func (fd *FD) getFileCompletionModes() (uint32, error) {
559 var info windows.FILE_IO_COMPLETION_NOTIFICATION_INFORMATION
560 err := windows.NtQueryInformationFile(fd.Sysfd, &windows.IO_STATUS_BLOCK{},
561 unsafe.Pointer(&info), uint32(unsafe.Sizeof(info)), windows.FileIoCompletionNotificationInformation)
562 return info.Flags, err
563 }
564
565
566
567 func (fd *FD) setFileCompletionModes() uint32 {
568
569
570
571 modes := uint8(syscall.FILE_SKIP_SET_EVENT_ON_HANDLE)
572 if canSkipCompletionPortOnSuccess(fd.Sysfd, fd.kind == kindNet) {
573 modes |= syscall.FILE_SKIP_COMPLETION_PORT_ON_SUCCESS
574 }
575 if syscall.SetFileCompletionNotificationModes(fd.Sysfd, modes) != nil {
576
577
578 return 0
579 }
580 return uint32(modes)
581 }
582
583
584
585
586 func (fd *FD) DisassociateIOCP() error {
587
588
589
590 if ok, err := fd.tryReadWriteLock(); err != nil || !ok {
591 if err == nil {
592 err = errors.New("can't disassociate the handle while there is in-progress I/O")
593 }
594 return err
595 }
596 defer fd.readWriteUnlock()
597
598 if fd.lazyInit {
599 if !fd.initMu.TryLock() {
600
601 return errors.New("can't disassociate the handle while initialization is in progress")
602 }
603 defer fd.initMu.Unlock()
604
605
606 fd.skipIOCP = true
607 }
608 if !fd.associated {
609
610 return nil
611 }
612
613 info := windows.FILE_COMPLETION_INFORMATION{}
614 if err := windows.NtSetInformationFile(fd.Sysfd, &windows.IO_STATUS_BLOCK{}, unsafe.Pointer(&info), uint32(unsafe.Sizeof(info)), windows.FileReplaceCompletionInformation); err != nil {
615 return err
616 }
617
618 fd.associated = false
619
620
621 return nil
622 }
623
624 func (fd *FD) destroy() error {
625 if fd.Sysfd == syscall.InvalidHandle {
626 return syscall.EINVAL
627 }
628
629
630 fd.pd.close()
631 var err error
632 switch fd.kind {
633 case kindNet:
634
635 err = CloseFunc(fd.Sysfd)
636 default:
637 err = syscall.CloseHandle(fd.Sysfd)
638 }
639 fd.Sysfd = syscall.InvalidHandle
640 runtime_Semrelease(&fd.csema)
641 return err
642 }
643
644
645
646 func (fd *FD) Close() error {
647 if !fd.fdmu.increfAndClose() {
648 return errClosing(fd.isFile)
649 }
650
651 if fd.kind == kindPipe {
652 syscall.CancelIoEx(fd.Sysfd, nil)
653 }
654
655 if fd.lazyInit {
656
657 fd.initMu.Lock()
658 fd.pd.evict()
659 fd.initMu.Unlock()
660 } else {
661 fd.pd.evict()
662 }
663 err := fd.decref()
664
665
666 runtime_Semacquire(&fd.csema)
667 return err
668 }
669
670
671
672
673 const maxRW = 1 << 30
674
675 func pinPtrsFromBuf(buf []byte) []any {
676 if len(buf) == 0 {
677 return nil
678 }
679 return []any{unsafe.SliceData(buf)}
680 }
681
682
683 func (fd *FD) Read(buf []byte) (int, error) {
684 if fd.kind == kindFile {
685 if err := fd.readWriteLock(); err != nil {
686 return 0, err
687 }
688 defer fd.readWriteUnlock()
689 } else {
690 if err := fd.readLock(); err != nil {
691 return 0, err
692 }
693 defer fd.readUnlock()
694 }
695
696 if len(buf) > maxRW {
697 buf = buf[:maxRW]
698 }
699
700 var n int
701 var err error
702 switch fd.kind {
703 case kindConsole:
704 n, err = fd.readConsole(buf)
705 case kindFile, kindPipe:
706 n, err = fd.execIO('r', func(o *operation) (qty uint32, err error) {
707 if fd.kind == kindFile {
708 o.setOffset(fd.offset)
709 }
710 err = syscall.ReadFile(fd.Sysfd, buf, &qty, fd.overlapped(o))
711 return qty, err
712 }, pinPtrsFromBuf(buf)...)
713 if fd.kind == kindFile {
714 fd.addOffset(n)
715 }
716 switch err {
717 case syscall.ERROR_HANDLE_EOF:
718 err = io.EOF
719 case syscall.ERROR_BROKEN_PIPE:
720
721 if fd.kind == kindPipe {
722 err = io.EOF
723 }
724 }
725 case kindNet:
726 n, err = fd.execIO('r', func(o *operation) (qty uint32, err error) {
727 var flags uint32
728 err = syscall.WSARecv(fd.Sysfd, newWsaBuf(buf), 1, &qty, &flags, &o.o, nil)
729 return qty, err
730 }, pinPtrsFromBuf(buf)...)
731 if race.Enabled {
732 race.Acquire(unsafe.Pointer(&ioSync))
733 }
734 }
735 if len(buf) != 0 {
736 err = fd.eofError(n, err)
737 }
738 return n, err
739 }
740
741 var ReadConsole = syscall.ReadConsole
742
743
744
745
746 func (fd *FD) readConsole(b []byte) (int, error) {
747 if len(b) == 0 {
748 return 0, nil
749 }
750
751 if fd.readuint16 == nil {
752
753
754
755 fd.readuint16 = make([]uint16, 0, 10000)
756 fd.readbyte = make([]byte, 0, 4*cap(fd.readuint16))
757 }
758
759 for fd.readbyteOffset >= len(fd.readbyte) {
760 n := cap(fd.readuint16) - len(fd.readuint16)
761 if n > len(b) {
762 n = len(b)
763 }
764 var nw uint32
765 err := ReadConsole(fd.Sysfd, &fd.readuint16[:len(fd.readuint16)+1][len(fd.readuint16)], uint32(n), &nw, nil)
766 if err != nil {
767 return 0, err
768 }
769 uint16s := fd.readuint16[:len(fd.readuint16)+int(nw)]
770 fd.readuint16 = fd.readuint16[:0]
771 buf := fd.readbyte[:0]
772 for i := 0; i < len(uint16s); i++ {
773 r := rune(uint16s[i])
774 if utf16.IsSurrogate(r) {
775 if i+1 == len(uint16s) {
776 if nw > 0 {
777
778 fd.readuint16 = fd.readuint16[:1]
779 fd.readuint16[0] = uint16(r)
780 break
781 }
782 r = utf8.RuneError
783 } else {
784 r = utf16.DecodeRune(r, rune(uint16s[i+1]))
785 if r != utf8.RuneError {
786 i++
787 }
788 }
789 }
790 buf = utf8.AppendRune(buf, r)
791 }
792 fd.readbyte = buf
793 fd.readbyteOffset = 0
794 if nw == 0 {
795 break
796 }
797 }
798
799 src := fd.readbyte[fd.readbyteOffset:]
800 var i int
801 for i = 0; i < len(src) && i < len(b); i++ {
802 x := src[i]
803 if x == 0x1A {
804 if i == 0 {
805 fd.readbyteOffset++
806 }
807 break
808 }
809 b[i] = x
810 }
811 fd.readbyteOffset += i
812 return i, nil
813 }
814
815
816 func (fd *FD) Pread(buf []byte, off int64) (int, error) {
817 if fd.kind == kindPipe {
818
819 return 0, syscall.ESPIPE
820 }
821
822 if err := fd.readWriteLock(); err != nil {
823 return 0, err
824 }
825 defer fd.readWriteUnlock()
826
827 if len(buf) > maxRW {
828 buf = buf[:maxRW]
829 }
830
831 n, err := fd.execIO('r', func(o *operation) (qty uint32, err error) {
832
833
834
835
836
837 if fd.isBlocking {
838 curoffset, err := syscall.Seek(fd.Sysfd, 0, io.SeekCurrent)
839 if err != nil {
840 return 0, err
841 }
842 defer syscall.Seek(fd.Sysfd, curoffset, io.SeekStart)
843 }
844 o.setOffset(off)
845
846 err = syscall.ReadFile(fd.Sysfd, buf, &qty, &o.o)
847 return qty, err
848 }, pinPtrsFromBuf(buf)...)
849 if err == syscall.ERROR_HANDLE_EOF {
850 err = io.EOF
851 }
852 if len(buf) != 0 {
853 err = fd.eofError(n, err)
854 }
855 return n, err
856 }
857
858
859 func (fd *FD) ReadFrom(buf []byte) (int, syscall.Sockaddr, error) {
860 if len(buf) == 0 {
861 return 0, nil, nil
862 }
863 if len(buf) > maxRW {
864 buf = buf[:maxRW]
865 }
866 if err := fd.readLock(); err != nil {
867 return 0, nil, err
868 }
869 defer fd.readUnlock()
870
871 rsa := newWSARsa()
872 defer wsaRsaPool.Put(rsa)
873 n, err := fd.execIO('r', func(o *operation) (qty uint32, err error) {
874 var flags uint32
875 err = syscall.WSARecvFrom(fd.Sysfd, newWsaBuf(buf), 1, &qty, &flags, &rsa.name, &rsa.namelen, &o.o, nil)
876 return qty, err
877 }, unsafe.SliceData(buf), rsa)
878 err = fd.eofError(n, err)
879 if err != nil {
880 return n, nil, err
881 }
882 sa, _ := rsa.name.Sockaddr()
883 return n, sa, nil
884 }
885
886
887 func (fd *FD) ReadFromInet4(buf []byte, sa4 *syscall.SockaddrInet4) (int, error) {
888 if len(buf) == 0 {
889 return 0, nil
890 }
891 if len(buf) > maxRW {
892 buf = buf[:maxRW]
893 }
894 if err := fd.readLock(); err != nil {
895 return 0, err
896 }
897 defer fd.readUnlock()
898
899 rsa := newWSARsa()
900 defer wsaRsaPool.Put(rsa)
901 n, err := fd.execIO('r', func(o *operation) (qty uint32, err error) {
902 var flags uint32
903 err = syscall.WSARecvFrom(fd.Sysfd, newWsaBuf(buf), 1, &qty, &flags, &rsa.name, &rsa.namelen, &o.o, nil)
904 return qty, err
905 }, unsafe.SliceData(buf), rsa)
906 err = fd.eofError(n, err)
907 if err != nil {
908 return n, err
909 }
910 rawToSockaddrInet4(&rsa.name, sa4)
911 return n, err
912 }
913
914
915 func (fd *FD) ReadFromInet6(buf []byte, sa6 *syscall.SockaddrInet6) (int, error) {
916 if len(buf) == 0 {
917 return 0, nil
918 }
919 if len(buf) > maxRW {
920 buf = buf[:maxRW]
921 }
922 if err := fd.readLock(); err != nil {
923 return 0, err
924 }
925 defer fd.readUnlock()
926
927 rsa := newWSARsa()
928 defer wsaRsaPool.Put(rsa)
929 n, err := fd.execIO('r', func(o *operation) (qty uint32, err error) {
930 var flags uint32
931 err = syscall.WSARecvFrom(fd.Sysfd, newWsaBuf(buf), 1, &qty, &flags, &rsa.name, &rsa.namelen, &o.o, nil)
932 return qty, err
933 }, unsafe.SliceData(buf), rsa)
934 err = fd.eofError(n, err)
935 if err != nil {
936 return n, err
937 }
938 rawToSockaddrInet6(&rsa.name, sa6)
939 return n, err
940 }
941
942
943 func (fd *FD) Write(buf []byte) (int, error) {
944 if fd.kind == kindFile {
945 if err := fd.readWriteLock(); err != nil {
946 return 0, err
947 }
948 defer fd.readWriteUnlock()
949 } else {
950 if err := fd.writeLock(); err != nil {
951 return 0, err
952 }
953 defer fd.writeUnlock()
954 }
955
956 var ntotal int
957 for {
958 max := len(buf)
959 if max-ntotal > maxRW {
960 max = ntotal + maxRW
961 }
962 b := buf[ntotal:max]
963 var n int
964 var err error
965 switch fd.kind {
966 case kindConsole:
967 n, err = fd.writeConsole(b)
968 case kindPipe, kindFile:
969 n, err = fd.execIO('w', func(o *operation) (qty uint32, err error) {
970 if fd.kind == kindFile {
971 o.setOffset(fd.offset)
972 }
973 err = syscall.WriteFile(fd.Sysfd, b, &qty, fd.overlapped(o))
974 return qty, err
975 }, pinPtrsFromBuf(b)...)
976 if fd.kind == kindFile {
977 fd.addOffset(n)
978 }
979 case kindNet:
980 if race.Enabled {
981 race.ReleaseMerge(unsafe.Pointer(&ioSync))
982 }
983 n, err = fd.execIO('w', func(o *operation) (qty uint32, err error) {
984 err = syscall.WSASend(fd.Sysfd, newWsaBuf(b), 1, &qty, 0, &o.o, nil)
985 return qty, err
986 }, pinPtrsFromBuf(b)...)
987 }
988 ntotal += n
989 if ntotal == len(buf) || err != nil {
990 return ntotal, err
991 }
992 if n == 0 {
993 return ntotal, io.ErrUnexpectedEOF
994 }
995 }
996 }
997
998
999
1000 func (fd *FD) writeConsole(b []byte) (int, error) {
1001 n := len(b)
1002 runes := make([]rune, 0, 256)
1003 if len(fd.lastbits) > 0 {
1004 b = append(fd.lastbits, b...)
1005 fd.lastbits = nil
1006
1007 }
1008 for len(b) >= utf8.UTFMax || utf8.FullRune(b) {
1009 r, l := utf8.DecodeRune(b)
1010 runes = append(runes, r)
1011 b = b[l:]
1012 }
1013 if len(b) > 0 {
1014 fd.lastbits = make([]byte, len(b))
1015 copy(fd.lastbits, b)
1016 }
1017
1018
1019
1020 const maxWrite = 16000
1021 for len(runes) > 0 {
1022 m := len(runes)
1023 if m > maxWrite {
1024 m = maxWrite
1025 }
1026 chunk := runes[:m]
1027 runes = runes[m:]
1028 uint16s := utf16.Encode(chunk)
1029 for len(uint16s) > 0 {
1030 var written uint32
1031 err := syscall.WriteConsole(fd.Sysfd, &uint16s[0], uint32(len(uint16s)), &written, nil)
1032 if err != nil {
1033 return 0, err
1034 }
1035 uint16s = uint16s[written:]
1036 }
1037 }
1038 return n, nil
1039 }
1040
1041
1042 func (fd *FD) Pwrite(buf []byte, off int64) (int, error) {
1043 if fd.kind == kindPipe {
1044
1045 return 0, syscall.ESPIPE
1046 }
1047
1048 if err := fd.readWriteLock(); err != nil {
1049 return 0, err
1050 }
1051 defer fd.readWriteUnlock()
1052
1053 var ntotal int
1054 for {
1055 max := len(buf)
1056 if max-ntotal > maxRW {
1057 max = ntotal + maxRW
1058 }
1059 b := buf[ntotal:max]
1060 n, err := fd.execIO('w', func(o *operation) (qty uint32, err error) {
1061
1062
1063
1064
1065
1066 if fd.isBlocking {
1067 curoffset, err := syscall.Seek(fd.Sysfd, 0, io.SeekCurrent)
1068 if err != nil {
1069 return 0, err
1070 }
1071 defer syscall.Seek(fd.Sysfd, curoffset, io.SeekStart)
1072 }
1073 o.setOffset(off + int64(ntotal))
1074
1075 err = syscall.WriteFile(fd.Sysfd, b, &qty, &o.o)
1076 return qty, err
1077 }, pinPtrsFromBuf(b)...)
1078 if n > 0 {
1079 ntotal += n
1080 }
1081 if ntotal == len(buf) || err != nil {
1082 return ntotal, err
1083 }
1084 if n == 0 {
1085 return ntotal, io.ErrUnexpectedEOF
1086 }
1087 }
1088 }
1089
1090
1091 func (fd *FD) Writev(buf *[][]byte) (int64, error) {
1092 if len(*buf) == 0 {
1093 return 0, nil
1094 }
1095 if err := fd.writeLock(); err != nil {
1096 return 0, err
1097 }
1098 defer fd.writeUnlock()
1099 if race.Enabled {
1100 race.ReleaseMerge(unsafe.Pointer(&ioSync))
1101 }
1102 bufs := newWSABufs(buf)
1103 defer freeWSABufs(bufs)
1104 n, err := fd.execIO('w', func(o *operation) (qty uint32, err error) {
1105 err = syscall.WSASend(fd.Sysfd, &(*bufs)[0], uint32(len(*bufs)), &qty, 0, &o.o, nil)
1106 return qty, err
1107 })
1108 TestHookDidWritev(n)
1109 consume(buf, int64(n))
1110 return int64(n), err
1111 }
1112
1113
1114 func (fd *FD) WriteTo(buf []byte, sa syscall.Sockaddr) (int, error) {
1115 if err := fd.writeLock(); err != nil {
1116 return 0, err
1117 }
1118 defer fd.writeUnlock()
1119
1120 if len(buf) == 0 {
1121
1122 n, err := fd.execIO('w', func(o *operation) (qty uint32, err error) {
1123 err = syscall.WSASendto(fd.Sysfd, &syscall.WSABuf{}, 1, &qty, 0, sa, &o.o, nil)
1124 return qty, err
1125 })
1126 return n, err
1127 }
1128
1129 ntotal := 0
1130 for len(buf) > 0 {
1131 b := buf
1132 if len(b) > maxRW {
1133 b = b[:maxRW]
1134 }
1135 n, err := fd.execIO('w', func(o *operation) (qty uint32, err error) {
1136 err = syscall.WSASendto(fd.Sysfd, newWsaBuf(b), 1, &qty, 0, sa, &o.o, nil)
1137 return qty, err
1138 }, unsafe.SliceData(b))
1139 ntotal += int(n)
1140 if err != nil {
1141 return ntotal, err
1142 }
1143 buf = buf[n:]
1144 }
1145 return ntotal, nil
1146 }
1147
1148
1149 func (fd *FD) WriteToInet4(buf []byte, sa4 *syscall.SockaddrInet4) (int, error) {
1150 if err := fd.writeLock(); err != nil {
1151 return 0, err
1152 }
1153 defer fd.writeUnlock()
1154
1155 if len(buf) == 0 {
1156
1157 n, err := fd.execIO('w', func(o *operation) (qty uint32, err error) {
1158 err = windows.WSASendtoInet4(fd.Sysfd, &syscall.WSABuf{}, 1, &qty, 0, sa4, &o.o, nil)
1159 return qty, err
1160 })
1161 return n, err
1162 }
1163
1164 ntotal := 0
1165 for len(buf) > 0 {
1166 b := buf
1167 if len(b) > maxRW {
1168 b = b[:maxRW]
1169 }
1170 n, err := fd.execIO('w', func(o *operation) (qty uint32, err error) {
1171 err = windows.WSASendtoInet4(fd.Sysfd, newWsaBuf(b), 1, &qty, 0, sa4, &o.o, nil)
1172 return qty, err
1173 }, unsafe.SliceData(b))
1174 ntotal += int(n)
1175 if err != nil {
1176 return ntotal, err
1177 }
1178 buf = buf[n:]
1179 }
1180 return ntotal, nil
1181 }
1182
1183
1184 func (fd *FD) WriteToInet6(buf []byte, sa6 *syscall.SockaddrInet6) (int, error) {
1185 if err := fd.writeLock(); err != nil {
1186 return 0, err
1187 }
1188 defer fd.writeUnlock()
1189
1190 if len(buf) == 0 {
1191
1192 n, err := fd.execIO('w', func(o *operation) (qty uint32, err error) {
1193 err = windows.WSASendtoInet6(fd.Sysfd, &syscall.WSABuf{}, 1, &qty, 0, sa6, &o.o, nil)
1194 return qty, err
1195 })
1196 return n, err
1197 }
1198
1199 ntotal := 0
1200 for len(buf) > 0 {
1201 b := buf
1202 if len(b) > maxRW {
1203 b = b[:maxRW]
1204 }
1205 n, err := fd.execIO('w', func(o *operation) (qty uint32, err error) {
1206 err = windows.WSASendtoInet6(fd.Sysfd, newWsaBuf(b), 1, &qty, 0, sa6, &o.o, nil)
1207 return qty, err
1208 }, unsafe.SliceData(b))
1209 ntotal += int(n)
1210 if err != nil {
1211 return ntotal, err
1212 }
1213 buf = buf[n:]
1214 }
1215 return ntotal, nil
1216 }
1217
1218
1219
1220
1221 func (fd *FD) ConnectEx(ra syscall.Sockaddr) error {
1222 _, err := fd.execIO('w', func(o *operation) (uint32, error) {
1223 return 0, ConnectExFunc(fd.Sysfd, ra, nil, 0, nil, &o.o)
1224 })
1225 return err
1226 }
1227
1228 func (fd *FD) acceptOne(s syscall.Handle, rawsa []syscall.RawSockaddrAny) (string, error) {
1229
1230 rsan := uint32(unsafe.Sizeof(rawsa[0]))
1231 _, err := fd.execIO('r', func(o *operation) (qty uint32, err error) {
1232 err = AcceptFunc(fd.Sysfd, s, (*byte)(unsafe.Pointer(&rawsa[0])), 0, rsan, rsan, &qty, &o.o)
1233 return qty, err
1234
1235 })
1236 if err != nil {
1237 CloseFunc(s)
1238 return "acceptex", err
1239 }
1240
1241
1242 err = syscall.Setsockopt(s, syscall.SOL_SOCKET, syscall.SO_UPDATE_ACCEPT_CONTEXT, (*byte)(unsafe.Pointer(&fd.Sysfd)), int32(unsafe.Sizeof(fd.Sysfd)))
1243 if err != nil {
1244 CloseFunc(s)
1245 return "setsockopt", err
1246 }
1247
1248 return "", nil
1249 }
1250
1251
1252
1253 func (fd *FD) Accept(sysSocket func() (syscall.Handle, error)) (syscall.Handle, []syscall.RawSockaddrAny, uint32, string, error) {
1254 if err := fd.readLock(); err != nil {
1255 return syscall.InvalidHandle, nil, 0, "", err
1256 }
1257 defer fd.readUnlock()
1258
1259 var rawsa [2]syscall.RawSockaddrAny
1260 for {
1261 s, err := sysSocket()
1262 if err != nil {
1263 return syscall.InvalidHandle, nil, 0, "", err
1264 }
1265
1266 errcall, err := fd.acceptOne(s, rawsa[:])
1267 if err == nil {
1268 return s, rawsa[:], uint32(unsafe.Sizeof(rawsa[0])), "", nil
1269 }
1270
1271
1272
1273
1274
1275
1276 errno, ok := err.(syscall.Errno)
1277 if !ok {
1278 return syscall.InvalidHandle, nil, 0, errcall, err
1279 }
1280 switch errno {
1281 case syscall.ERROR_NETNAME_DELETED, syscall.WSAECONNRESET:
1282
1283 default:
1284 return syscall.InvalidHandle, nil, 0, errcall, err
1285 }
1286 }
1287 }
1288
1289
1290 func (fd *FD) Seek(offset int64, whence int) (int64, error) {
1291 if fd.kind == kindPipe {
1292 return 0, syscall.ESPIPE
1293 }
1294 if err := fd.readWriteLock(); err != nil {
1295 return 0, err
1296 }
1297 defer fd.readWriteUnlock()
1298
1299 if err := fd.ensureInit(); err != nil {
1300 return 0, err
1301 }
1302 if !fd.isBlocking {
1303
1304
1305 var newOffset int64
1306 switch whence {
1307 case io.SeekStart:
1308 newOffset = offset
1309 case io.SeekCurrent:
1310 newOffset = fd.offset + offset
1311 case io.SeekEnd:
1312 var size int64
1313 if err := windows.GetFileSizeEx(fd.Sysfd, &size); err != nil {
1314 return 0, err
1315 }
1316 newOffset = size + offset
1317 default:
1318 return 0, windows.ERROR_INVALID_PARAMETER
1319 }
1320 if newOffset < 0 {
1321 return 0, windows.ERROR_NEGATIVE_SEEK
1322 }
1323 fd.setOffset(newOffset)
1324 return newOffset, nil
1325 }
1326 n, err := syscall.Seek(fd.Sysfd, offset, whence)
1327 fd.setOffset(n)
1328 return n, err
1329 }
1330
1331
1332 func (fd *FD) Fchmod(mode uint32) error {
1333 if err := fd.incref(); err != nil {
1334 return err
1335 }
1336 defer fd.decref()
1337
1338 var d syscall.ByHandleFileInformation
1339 if err := syscall.GetFileInformationByHandle(fd.Sysfd, &d); err != nil {
1340 return err
1341 }
1342 attrs := d.FileAttributes
1343 if mode&syscall.S_IWRITE != 0 {
1344 attrs &^= syscall.FILE_ATTRIBUTE_READONLY
1345 } else {
1346 attrs |= syscall.FILE_ATTRIBUTE_READONLY
1347 }
1348 if attrs == d.FileAttributes {
1349 return nil
1350 }
1351
1352 var du windows.FILE_BASIC_INFO
1353 du.FileAttributes = attrs
1354 return windows.SetFileInformationByHandle(fd.Sysfd, windows.FileBasicInfo, unsafe.Pointer(&du), uint32(unsafe.Sizeof(du)))
1355 }
1356
1357
1358 func (fd *FD) Fchdir() error {
1359 if err := fd.incref(); err != nil {
1360 return err
1361 }
1362 defer fd.decref()
1363 return syscall.Fchdir(fd.Sysfd)
1364 }
1365
1366
1367 func (fd *FD) GetFileType() (uint32, error) {
1368 if err := fd.incref(); err != nil {
1369 return 0, err
1370 }
1371 defer fd.decref()
1372 return syscall.GetFileType(fd.Sysfd)
1373 }
1374
1375
1376 func (fd *FD) GetFileInformationByHandle(data *syscall.ByHandleFileInformation) error {
1377 if err := fd.incref(); err != nil {
1378 return err
1379 }
1380 defer fd.decref()
1381 return syscall.GetFileInformationByHandle(fd.Sysfd, data)
1382 }
1383
1384
1385 func (fd *FD) RawRead(f func(uintptr) bool) error {
1386 if err := fd.readLock(); err != nil {
1387 return err
1388 }
1389 defer fd.readUnlock()
1390 for {
1391 if f(uintptr(fd.Sysfd)) {
1392 return nil
1393 }
1394
1395
1396
1397 _, err := fd.execIO('r', func(o *operation) (qty uint32, err error) {
1398 var flags uint32
1399 if !fd.IsStream {
1400 flags |= windows.MSG_PEEK
1401 }
1402 err = syscall.WSARecv(fd.Sysfd, &syscall.WSABuf{}, 1, &qty, &flags, &o.o, nil)
1403 return qty, err
1404 })
1405 if err == windows.WSAEMSGSIZE {
1406
1407 } else if err != nil {
1408 return err
1409 }
1410 }
1411 }
1412
1413
1414 func (fd *FD) RawWrite(f func(uintptr) bool) error {
1415 if err := fd.writeLock(); err != nil {
1416 return err
1417 }
1418 defer fd.writeUnlock()
1419
1420 if f(uintptr(fd.Sysfd)) {
1421 return nil
1422 }
1423
1424
1425 return syscall.EWINDOWS
1426 }
1427
1428 func sockaddrInet4ToRaw(rsa *syscall.RawSockaddrAny, sa *syscall.SockaddrInet4) int32 {
1429 *rsa = syscall.RawSockaddrAny{}
1430 raw := (*syscall.RawSockaddrInet4)(unsafe.Pointer(rsa))
1431 raw.Family = syscall.AF_INET
1432 p := (*[2]byte)(unsafe.Pointer(&raw.Port))
1433 p[0] = byte(sa.Port >> 8)
1434 p[1] = byte(sa.Port)
1435 raw.Addr = sa.Addr
1436 return int32(unsafe.Sizeof(*raw))
1437 }
1438
1439 func sockaddrInet6ToRaw(rsa *syscall.RawSockaddrAny, sa *syscall.SockaddrInet6) int32 {
1440 *rsa = syscall.RawSockaddrAny{}
1441 raw := (*syscall.RawSockaddrInet6)(unsafe.Pointer(rsa))
1442 raw.Family = syscall.AF_INET6
1443 p := (*[2]byte)(unsafe.Pointer(&raw.Port))
1444 p[0] = byte(sa.Port >> 8)
1445 p[1] = byte(sa.Port)
1446 raw.Scope_id = sa.ZoneId
1447 raw.Addr = sa.Addr
1448 return int32(unsafe.Sizeof(*raw))
1449 }
1450
1451 func rawToSockaddrInet4(rsa *syscall.RawSockaddrAny, sa *syscall.SockaddrInet4) {
1452 pp := (*syscall.RawSockaddrInet4)(unsafe.Pointer(rsa))
1453 p := (*[2]byte)(unsafe.Pointer(&pp.Port))
1454 sa.Port = int(p[0])<<8 + int(p[1])
1455 sa.Addr = pp.Addr
1456 }
1457
1458 func rawToSockaddrInet6(rsa *syscall.RawSockaddrAny, sa *syscall.SockaddrInet6) {
1459 pp := (*syscall.RawSockaddrInet6)(unsafe.Pointer(rsa))
1460 p := (*[2]byte)(unsafe.Pointer(&pp.Port))
1461 sa.Port = int(p[0])<<8 + int(p[1])
1462 sa.ZoneId = pp.Scope_id
1463 sa.Addr = pp.Addr
1464 }
1465
1466 func sockaddrToRaw(rsa *syscall.RawSockaddrAny, sa syscall.Sockaddr) (int32, error) {
1467 switch sa := sa.(type) {
1468 case *syscall.SockaddrInet4:
1469 sz := sockaddrInet4ToRaw(rsa, sa)
1470 return sz, nil
1471 case *syscall.SockaddrInet6:
1472 sz := sockaddrInet6ToRaw(rsa, sa)
1473 return sz, nil
1474 default:
1475 return 0, syscall.EWINDOWS
1476 }
1477 }
1478
1479
1480 func (fd *FD) ReadMsg(p []byte, oob []byte, flags int) (int, int, int, syscall.Sockaddr, error) {
1481 if err := fd.readLock(); err != nil {
1482 return 0, 0, 0, nil, err
1483 }
1484 defer fd.readUnlock()
1485
1486 if len(p) > maxRW {
1487 p = p[:maxRW]
1488 }
1489
1490 rsa := newWSARsa()
1491 defer wsaRsaPool.Put(rsa)
1492 msg := newWSAMsg(p, oob, flags, rsa)
1493 defer freeWSAMsg(msg)
1494 n, err := fd.execIO('r', func(o *operation) (qty uint32, err error) {
1495 err = windows.WSARecvMsg(fd.Sysfd, msg, &qty, &o.o, nil)
1496 return qty, err
1497 }, rsa, msg)
1498 err = fd.eofError(n, err)
1499 var sa syscall.Sockaddr
1500 if err == nil {
1501 sa, err = msg.Name.Sockaddr()
1502 }
1503 return n, int(msg.Control.Len), int(msg.Flags), sa, err
1504 }
1505
1506
1507 func (fd *FD) ReadMsgInet4(p []byte, oob []byte, flags int, sa4 *syscall.SockaddrInet4) (int, int, int, error) {
1508 if err := fd.readLock(); err != nil {
1509 return 0, 0, 0, err
1510 }
1511 defer fd.readUnlock()
1512
1513 if len(p) > maxRW {
1514 p = p[:maxRW]
1515 }
1516
1517 rsa := newWSARsa()
1518 defer wsaRsaPool.Put(rsa)
1519 msg := newWSAMsg(p, oob, flags, rsa)
1520 defer freeWSAMsg(msg)
1521 n, err := fd.execIO('r', func(o *operation) (qty uint32, err error) {
1522 err = windows.WSARecvMsg(fd.Sysfd, msg, &qty, &o.o, nil)
1523 return qty, err
1524 }, rsa, msg)
1525 err = fd.eofError(n, err)
1526 if err == nil {
1527 rawToSockaddrInet4(msg.Name, sa4)
1528 }
1529 return n, int(msg.Control.Len), int(msg.Flags), err
1530 }
1531
1532
1533 func (fd *FD) ReadMsgInet6(p []byte, oob []byte, flags int, sa6 *syscall.SockaddrInet6) (int, int, int, error) {
1534 if err := fd.readLock(); err != nil {
1535 return 0, 0, 0, err
1536 }
1537 defer fd.readUnlock()
1538
1539 if len(p) > maxRW {
1540 p = p[:maxRW]
1541 }
1542
1543 rsa := newWSARsa()
1544 defer wsaRsaPool.Put(rsa)
1545 msg := newWSAMsg(p, oob, flags, rsa)
1546 defer freeWSAMsg(msg)
1547 n, err := fd.execIO('r', func(o *operation) (qty uint32, err error) {
1548 err = windows.WSARecvMsg(fd.Sysfd, msg, &qty, &o.o, nil)
1549 return qty, err
1550 }, rsa, msg)
1551 err = fd.eofError(n, err)
1552 if err == nil {
1553 rawToSockaddrInet6(msg.Name, sa6)
1554 }
1555 return n, int(msg.Control.Len), int(msg.Flags), err
1556 }
1557
1558
1559 func (fd *FD) WriteMsg(p []byte, oob []byte, sa syscall.Sockaddr) (int, int, error) {
1560 if len(p) > maxRW {
1561 return 0, 0, errors.New("packet is too large (only 1GB is allowed)")
1562 }
1563
1564 if err := fd.writeLock(); err != nil {
1565 return 0, 0, err
1566 }
1567 defer fd.writeUnlock()
1568
1569 var rsa *wsaRsa
1570 if sa != nil {
1571 rsa = newWSARsa()
1572 defer wsaRsaPool.Put(rsa)
1573 var err error
1574 rsa.namelen, err = sockaddrToRaw(&rsa.name, sa)
1575 if err != nil {
1576 return 0, 0, err
1577 }
1578 }
1579 msg := newWSAMsg(p, oob, 0, rsa)
1580 defer freeWSAMsg(msg)
1581 n, err := fd.execIO('w', func(o *operation) (qty uint32, err error) {
1582 err = windows.WSASendMsg(fd.Sysfd, msg, 0, nil, &o.o, nil)
1583 return qty, err
1584 }, rsa, msg)
1585 return n, int(msg.Control.Len), err
1586 }
1587
1588
1589 func (fd *FD) WriteMsgInet4(p []byte, oob []byte, sa *syscall.SockaddrInet4) (int, int, error) {
1590 if len(p) > maxRW {
1591 return 0, 0, errors.New("packet is too large (only 1GB is allowed)")
1592 }
1593
1594 if err := fd.writeLock(); err != nil {
1595 return 0, 0, err
1596 }
1597 defer fd.writeUnlock()
1598
1599 var rsa *wsaRsa
1600 if sa != nil {
1601 rsa = newWSARsa()
1602 defer wsaRsaPool.Put(rsa)
1603 rsa.namelen = sockaddrInet4ToRaw(&rsa.name, sa)
1604 }
1605 msg := newWSAMsg(p, oob, 0, rsa)
1606 defer freeWSAMsg(msg)
1607 n, err := fd.execIO('w', func(o *operation) (qty uint32, err error) {
1608 err = windows.WSASendMsg(fd.Sysfd, msg, 0, nil, &o.o, nil)
1609 return qty, err
1610 }, rsa, msg)
1611 return n, int(msg.Control.Len), err
1612 }
1613
1614
1615 func (fd *FD) WriteMsgInet6(p []byte, oob []byte, sa *syscall.SockaddrInet6) (int, int, error) {
1616 if len(p) > maxRW {
1617 return 0, 0, errors.New("packet is too large (only 1GB is allowed)")
1618 }
1619
1620 if err := fd.writeLock(); err != nil {
1621 return 0, 0, err
1622 }
1623 defer fd.writeUnlock()
1624
1625 var rsa *wsaRsa
1626 if sa != nil {
1627 rsa = newWSARsa()
1628 defer wsaRsaPool.Put(rsa)
1629 rsa.namelen = sockaddrInet6ToRaw(&rsa.name, sa)
1630 }
1631 msg := newWSAMsg(p, oob, 0, rsa)
1632 defer freeWSAMsg(msg)
1633 n, err := fd.execIO('w', func(o *operation) (qty uint32, err error) {
1634 err = windows.WSASendMsg(fd.Sysfd, msg, 0, nil, &o.o, nil)
1635 return qty, err
1636 }, rsa, msg)
1637 return n, int(msg.Control.Len), err
1638 }
1639
1640 func DupCloseOnExec(fd int) (int, string, error) {
1641 proc, err := syscall.GetCurrentProcess()
1642 if err != nil {
1643 return 0, "GetCurrentProcess", err
1644 }
1645
1646 var nfd syscall.Handle
1647 const inherit = false
1648 if err := syscall.DuplicateHandle(proc, syscall.Handle(fd), proc, &nfd, 0, inherit, syscall.DUPLICATE_SAME_ACCESS); err != nil {
1649 return 0, "DuplicateHandle", err
1650 }
1651 return int(nfd), "", nil
1652 }
1653
View as plain text