fileutil_windows.go 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. // Copyright (c) 2014 The fileutil Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package fileutil
  5. import (
  6. "io"
  7. "os"
  8. "sync"
  9. "syscall"
  10. "unsafe"
  11. )
  12. // PunchHole deallocates space inside a file in the byte range starting at
  13. // offset and continuing for len bytes. Not supported on Windows.
  14. func PunchHole(f *os.File, off, len int64) error {
  15. return puncher(f, off, len)
  16. }
  17. // Fadvise predeclares an access pattern for file data. See also 'man 2
  18. // posix_fadvise'. Not supported on Windows.
  19. func Fadvise(f *os.File, off, len int64, advice FadviseAdvice) error {
  20. return nil
  21. }
  22. // IsEOF reports whether err is an EOF condition.
  23. func IsEOF(err error) bool {
  24. if err == io.EOF {
  25. return true
  26. }
  27. // http://social.technet.microsoft.com/Forums/windowsserver/en-US/1a16311b-c625-46cf-830b-6a26af488435/how-to-solve-error-38-0x26-errorhandleeof-using-fsctlgetretrievalpointers
  28. x, ok := err.(*os.PathError)
  29. return ok && x.Op == "read" && x.Err.(syscall.Errno) == 0x26
  30. }
  31. var (
  32. modkernel32 = syscall.NewLazyDLL("kernel32.dll")
  33. procDeviceIOControl = modkernel32.NewProc("DeviceIoControl")
  34. sparseFilesMu sync.Mutex
  35. sparseFiles map[uintptr]struct{}
  36. )
  37. func init() {
  38. // sparseFiles is an fd set for already "sparsed" files - according to
  39. // msdn.microsoft.com/en-us/library/windows/desktop/aa364225(v=vs.85).aspx
  40. // the file handles are unique per process.
  41. sparseFiles = make(map[uintptr]struct{})
  42. }
  43. // puncHoleWindows punches a hole into the given file starting at offset,
  44. // measuring "size" bytes
  45. // (http://msdn.microsoft.com/en-us/library/windows/desktop/aa364597%28v=vs.85%29.aspx)
  46. func puncher(file *os.File, offset, size int64) error {
  47. if err := ensureFileSparse(file); err != nil {
  48. return err
  49. }
  50. // http://msdn.microsoft.com/en-us/library/windows/desktop/aa364411%28v=vs.85%29.aspx
  51. // typedef struct _FILE_ZERO_DATA_INFORMATION {
  52. // LARGE_INTEGER FileOffset;
  53. // LARGE_INTEGER BeyondFinalZero;
  54. //} FILE_ZERO_DATA_INFORMATION, *PFILE_ZERO_DATA_INFORMATION;
  55. type fileZeroDataInformation struct {
  56. FileOffset, BeyondFinalZero int64
  57. }
  58. lpInBuffer := fileZeroDataInformation{
  59. FileOffset: offset,
  60. BeyondFinalZero: offset + size}
  61. return deviceIOControl(false, file.Fd(), uintptr(unsafe.Pointer(&lpInBuffer)), 16)
  62. }
  63. // // http://msdn.microsoft.com/en-us/library/windows/desktop/cc948908%28v=vs.85%29.aspx
  64. // type fileSetSparseBuffer struct {
  65. // SetSparse bool
  66. // }
  67. func ensureFileSparse(file *os.File) (err error) {
  68. fd := file.Fd()
  69. sparseFilesMu.Lock()
  70. if _, ok := sparseFiles[fd]; ok {
  71. sparseFilesMu.Unlock()
  72. return nil
  73. }
  74. if err = deviceIOControl(true, fd, 0, 0); err == nil {
  75. sparseFiles[fd] = struct{}{}
  76. }
  77. sparseFilesMu.Unlock()
  78. return err
  79. }
  80. func deviceIOControl(setSparse bool, fd, inBuf, inBufLen uintptr) (err error) {
  81. const (
  82. //http://source.winehq.org/source/include/winnt.h#L4605
  83. file_read_data = 1
  84. file_write_data = 2
  85. // METHOD_BUFFERED 0
  86. method_buffered = 0
  87. // FILE_ANY_ACCESS 0
  88. file_any_access = 0
  89. // FILE_DEVICE_FILE_SYSTEM 0x00000009
  90. file_device_file_system = 0x00000009
  91. // FILE_SPECIAL_ACCESS (FILE_ANY_ACCESS)
  92. file_special_access = file_any_access
  93. file_read_access = file_read_data
  94. file_write_access = file_write_data
  95. // http://source.winehq.org/source/include/winioctl.h
  96. // #define CTL_CODE ( DeviceType,
  97. // Function,
  98. // Method,
  99. // Access )
  100. // ((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method)
  101. // FSCTL_SET_COMPRESSION CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 16, METHOD_BUFFERED, FILE_READ_DATA | FILE_WRITE_DATA)
  102. fsctl_set_compression = (file_device_file_system << 16) | ((file_read_access | file_write_access) << 14) | (16 << 2) | method_buffered
  103. // FSCTL_SET_SPARSE CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 49, METHOD_BUFFERED, FILE_SPECIAL_ACCESS)
  104. fsctl_set_sparse = (file_device_file_system << 16) | (file_special_access << 14) | (49 << 2) | method_buffered
  105. // FSCTL_SET_ZERO_DATA CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 50, METHOD_BUFFERED, FILE_WRITE_DATA)
  106. fsctl_set_zero_data = (file_device_file_system << 16) | (file_write_data << 14) | (50 << 2) | method_buffered
  107. )
  108. retPtr := uintptr(unsafe.Pointer(&(make([]byte, 8)[0])))
  109. var r1 uintptr
  110. var e1 syscall.Errno
  111. if setSparse {
  112. // BOOL
  113. // WINAPI
  114. // DeviceIoControl( (HANDLE) hDevice, // handle to a file
  115. // FSCTL_SET_SPARSE, // dwIoControlCode
  116. // (PFILE_SET_SPARSE_BUFFER) lpInBuffer, // input buffer
  117. // (DWORD) nInBufferSize, // size of input buffer
  118. // NULL, // lpOutBuffer
  119. // 0, // nOutBufferSize
  120. // (LPDWORD) lpBytesReturned, // number of bytes returned
  121. // (LPOVERLAPPED) lpOverlapped ); // OVERLAPPED structure
  122. r1, _, e1 = syscall.Syscall9(procDeviceIOControl.Addr(), 8,
  123. fd,
  124. uintptr(fsctl_set_sparse),
  125. // If the lpInBuffer parameter is NULL, the operation will behave the same as if the SetSparse member of the FILE_SET_SPARSE_BUFFER structure were TRUE. In other words, the operation sets the file to a sparse file.
  126. 0, // uintptr(unsafe.Pointer(&lpInBuffer)),
  127. 0, // 1,
  128. 0,
  129. 0,
  130. retPtr,
  131. 0,
  132. 0)
  133. } else {
  134. // BOOL
  135. // WINAPI
  136. // DeviceIoControl( (HANDLE) hDevice, // handle to a file
  137. // FSCTL_SET_ZERO_DATA, // dwIoControlCode
  138. // (LPVOID) lpInBuffer, // input buffer
  139. // (DWORD) nInBufferSize, // size of input buffer
  140. // NULL, // lpOutBuffer
  141. // 0, // nOutBufferSize
  142. // (LPDWORD) lpBytesReturned, // number of bytes returned
  143. // (LPOVERLAPPED) lpOverlapped ); // OVERLAPPED structure
  144. r1, _, e1 = syscall.Syscall9(procDeviceIOControl.Addr(), 8,
  145. fd,
  146. uintptr(fsctl_set_zero_data),
  147. inBuf,
  148. inBufLen,
  149. 0,
  150. 0,
  151. retPtr,
  152. 0,
  153. 0)
  154. }
  155. if r1 == 0 {
  156. if e1 != 0 {
  157. err = error(e1)
  158. } else {
  159. err = syscall.EINVAL
  160. }
  161. }
  162. return err
  163. }