| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183 |
- // Copyright (c) 2014 The fileutil Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style
- // license that can be found in the LICENSE file.
- package fileutil
- import (
- "io"
- "os"
- "sync"
- "syscall"
- "unsafe"
- )
- // PunchHole deallocates space inside a file in the byte range starting at
- // offset and continuing for len bytes. Not supported on Windows.
- func PunchHole(f *os.File, off, len int64) error {
- return puncher(f, off, len)
- }
- // Fadvise predeclares an access pattern for file data. See also 'man 2
- // posix_fadvise'. Not supported on Windows.
- func Fadvise(f *os.File, off, len int64, advice FadviseAdvice) error {
- return nil
- }
- // IsEOF reports whether err is an EOF condition.
- func IsEOF(err error) bool {
- if err == io.EOF {
- return true
- }
- // http://social.technet.microsoft.com/Forums/windowsserver/en-US/1a16311b-c625-46cf-830b-6a26af488435/how-to-solve-error-38-0x26-errorhandleeof-using-fsctlgetretrievalpointers
- x, ok := err.(*os.PathError)
- return ok && x.Op == "read" && x.Err.(syscall.Errno) == 0x26
- }
- var (
- modkernel32 = syscall.NewLazyDLL("kernel32.dll")
- procDeviceIOControl = modkernel32.NewProc("DeviceIoControl")
- sparseFilesMu sync.Mutex
- sparseFiles map[uintptr]struct{}
- )
- func init() {
- // sparseFiles is an fd set for already "sparsed" files - according to
- // msdn.microsoft.com/en-us/library/windows/desktop/aa364225(v=vs.85).aspx
- // the file handles are unique per process.
- sparseFiles = make(map[uintptr]struct{})
- }
- // puncHoleWindows punches a hole into the given file starting at offset,
- // measuring "size" bytes
- // (http://msdn.microsoft.com/en-us/library/windows/desktop/aa364597%28v=vs.85%29.aspx)
- func puncher(file *os.File, offset, size int64) error {
- if err := ensureFileSparse(file); err != nil {
- return err
- }
- // http://msdn.microsoft.com/en-us/library/windows/desktop/aa364411%28v=vs.85%29.aspx
- // typedef struct _FILE_ZERO_DATA_INFORMATION {
- // LARGE_INTEGER FileOffset;
- // LARGE_INTEGER BeyondFinalZero;
- //} FILE_ZERO_DATA_INFORMATION, *PFILE_ZERO_DATA_INFORMATION;
- type fileZeroDataInformation struct {
- FileOffset, BeyondFinalZero int64
- }
- lpInBuffer := fileZeroDataInformation{
- FileOffset: offset,
- BeyondFinalZero: offset + size}
- return deviceIOControl(false, file.Fd(), uintptr(unsafe.Pointer(&lpInBuffer)), 16)
- }
- // // http://msdn.microsoft.com/en-us/library/windows/desktop/cc948908%28v=vs.85%29.aspx
- // type fileSetSparseBuffer struct {
- // SetSparse bool
- // }
- func ensureFileSparse(file *os.File) (err error) {
- fd := file.Fd()
- sparseFilesMu.Lock()
- if _, ok := sparseFiles[fd]; ok {
- sparseFilesMu.Unlock()
- return nil
- }
- if err = deviceIOControl(true, fd, 0, 0); err == nil {
- sparseFiles[fd] = struct{}{}
- }
- sparseFilesMu.Unlock()
- return err
- }
- func deviceIOControl(setSparse bool, fd, inBuf, inBufLen uintptr) (err error) {
- const (
- //http://source.winehq.org/source/include/winnt.h#L4605
- file_read_data = 1
- file_write_data = 2
- // METHOD_BUFFERED 0
- method_buffered = 0
- // FILE_ANY_ACCESS 0
- file_any_access = 0
- // FILE_DEVICE_FILE_SYSTEM 0x00000009
- file_device_file_system = 0x00000009
- // FILE_SPECIAL_ACCESS (FILE_ANY_ACCESS)
- file_special_access = file_any_access
- file_read_access = file_read_data
- file_write_access = file_write_data
- // http://source.winehq.org/source/include/winioctl.h
- // #define CTL_CODE ( DeviceType,
- // Function,
- // Method,
- // Access )
- // ((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method)
- // FSCTL_SET_COMPRESSION CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 16, METHOD_BUFFERED, FILE_READ_DATA | FILE_WRITE_DATA)
- fsctl_set_compression = (file_device_file_system << 16) | ((file_read_access | file_write_access) << 14) | (16 << 2) | method_buffered
- // FSCTL_SET_SPARSE CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 49, METHOD_BUFFERED, FILE_SPECIAL_ACCESS)
- fsctl_set_sparse = (file_device_file_system << 16) | (file_special_access << 14) | (49 << 2) | method_buffered
- // FSCTL_SET_ZERO_DATA CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 50, METHOD_BUFFERED, FILE_WRITE_DATA)
- fsctl_set_zero_data = (file_device_file_system << 16) | (file_write_data << 14) | (50 << 2) | method_buffered
- )
- retPtr := uintptr(unsafe.Pointer(&(make([]byte, 8)[0])))
- var r1 uintptr
- var e1 syscall.Errno
- if setSparse {
- // BOOL
- // WINAPI
- // DeviceIoControl( (HANDLE) hDevice, // handle to a file
- // FSCTL_SET_SPARSE, // dwIoControlCode
- // (PFILE_SET_SPARSE_BUFFER) lpInBuffer, // input buffer
- // (DWORD) nInBufferSize, // size of input buffer
- // NULL, // lpOutBuffer
- // 0, // nOutBufferSize
- // (LPDWORD) lpBytesReturned, // number of bytes returned
- // (LPOVERLAPPED) lpOverlapped ); // OVERLAPPED structure
- r1, _, e1 = syscall.Syscall9(procDeviceIOControl.Addr(), 8,
- fd,
- uintptr(fsctl_set_sparse),
- // 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.
- 0, // uintptr(unsafe.Pointer(&lpInBuffer)),
- 0, // 1,
- 0,
- 0,
- retPtr,
- 0,
- 0)
- } else {
- // BOOL
- // WINAPI
- // DeviceIoControl( (HANDLE) hDevice, // handle to a file
- // FSCTL_SET_ZERO_DATA, // dwIoControlCode
- // (LPVOID) lpInBuffer, // input buffer
- // (DWORD) nInBufferSize, // size of input buffer
- // NULL, // lpOutBuffer
- // 0, // nOutBufferSize
- // (LPDWORD) lpBytesReturned, // number of bytes returned
- // (LPOVERLAPPED) lpOverlapped ); // OVERLAPPED structure
- r1, _, e1 = syscall.Syscall9(procDeviceIOControl.Addr(), 8,
- fd,
- uintptr(fsctl_set_zero_data),
- inBuf,
- inBufLen,
- 0,
- 0,
- retPtr,
- 0,
- 0)
- }
- if r1 == 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = syscall.EINVAL
- }
- }
- return err
- }
|