syscall_windows.go 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. /*
  2. Copyright The containerd Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package syscallx
  14. import (
  15. "syscall"
  16. "unsafe"
  17. )
  18. type reparseDataBuffer struct {
  19. ReparseTag uint32
  20. ReparseDataLength uint16
  21. Reserved uint16
  22. // GenericReparseBuffer
  23. reparseBuffer byte
  24. }
  25. type mountPointReparseBuffer struct {
  26. SubstituteNameOffset uint16
  27. SubstituteNameLength uint16
  28. PrintNameOffset uint16
  29. PrintNameLength uint16
  30. PathBuffer [1]uint16
  31. }
  32. type symbolicLinkReparseBuffer struct {
  33. SubstituteNameOffset uint16
  34. SubstituteNameLength uint16
  35. PrintNameOffset uint16
  36. PrintNameLength uint16
  37. Flags uint32
  38. PathBuffer [1]uint16
  39. }
  40. const (
  41. _IO_REPARSE_TAG_MOUNT_POINT = 0xA0000003
  42. _SYMLINK_FLAG_RELATIVE = 1
  43. )
  44. // Readlink returns the destination of the named symbolic link.
  45. func Readlink(path string, buf []byte) (n int, err error) {
  46. fd, err := syscall.CreateFile(syscall.StringToUTF16Ptr(path), syscall.GENERIC_READ, 0, nil, syscall.OPEN_EXISTING,
  47. syscall.FILE_FLAG_OPEN_REPARSE_POINT|syscall.FILE_FLAG_BACKUP_SEMANTICS, 0)
  48. if err != nil {
  49. return -1, err
  50. }
  51. defer syscall.CloseHandle(fd)
  52. rdbbuf := make([]byte, syscall.MAXIMUM_REPARSE_DATA_BUFFER_SIZE)
  53. var bytesReturned uint32
  54. err = syscall.DeviceIoControl(fd, syscall.FSCTL_GET_REPARSE_POINT, nil, 0, &rdbbuf[0], uint32(len(rdbbuf)), &bytesReturned, nil)
  55. if err != nil {
  56. return -1, err
  57. }
  58. rdb := (*reparseDataBuffer)(unsafe.Pointer(&rdbbuf[0]))
  59. var s string
  60. switch rdb.ReparseTag {
  61. case syscall.IO_REPARSE_TAG_SYMLINK:
  62. data := (*symbolicLinkReparseBuffer)(unsafe.Pointer(&rdb.reparseBuffer))
  63. p := (*[0xffff]uint16)(unsafe.Pointer(&data.PathBuffer[0]))
  64. s = syscall.UTF16ToString(p[data.SubstituteNameOffset/2 : (data.SubstituteNameOffset+data.SubstituteNameLength)/2])
  65. if data.Flags&_SYMLINK_FLAG_RELATIVE == 0 {
  66. if len(s) >= 4 && s[:4] == `\??\` {
  67. s = s[4:]
  68. switch {
  69. case len(s) >= 2 && s[1] == ':': // \??\C:\foo\bar
  70. // do nothing
  71. case len(s) >= 4 && s[:4] == `UNC\`: // \??\UNC\foo\bar
  72. s = `\\` + s[4:]
  73. default:
  74. // unexpected; do nothing
  75. }
  76. } else {
  77. // unexpected; do nothing
  78. }
  79. }
  80. case _IO_REPARSE_TAG_MOUNT_POINT:
  81. data := (*mountPointReparseBuffer)(unsafe.Pointer(&rdb.reparseBuffer))
  82. p := (*[0xffff]uint16)(unsafe.Pointer(&data.PathBuffer[0]))
  83. s = syscall.UTF16ToString(p[data.SubstituteNameOffset/2 : (data.SubstituteNameOffset+data.SubstituteNameLength)/2])
  84. if len(s) >= 4 && s[:4] == `\??\` { // \??\C:\foo\bar
  85. if len(s) < 48 || s[:11] != `\??\Volume{` {
  86. s = s[4:]
  87. }
  88. } else {
  89. // unexpected; do nothing
  90. }
  91. default:
  92. // the path is not a symlink or junction but another type of reparse
  93. // point
  94. return -1, syscall.ENOENT
  95. }
  96. n = copy(buf, []byte(s))
  97. return n, nil
  98. }