github.com/alibaba/ilogtail/pkg@v0.0.0-20250526110833-c53b480d046c/helper/file_helper_windows.go (about)

     1  // Licensed to Elasticsearch B.V. under one or more contributor
     2  // license agreements. See the NOTICE file distributed with
     3  // this work for additional information regarding copyright
     4  // ownership. Elasticsearch B.V. licenses this file to you under
     5  // the Apache License, Version 2.0 (the "License"); you may
     6  // not use this file except in compliance with the License.
     7  // You may obtain a copy of the License at
     8  //
     9  //     http://www.apache.org/licenses/LICENSE-2.0
    10  //
    11  // Unless required by applicable law or agreed to in writing,
    12  // software distributed under the License is distributed on an
    13  // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    14  // KIND, either express or implied.  See the License for the
    15  // specific language governing permissions and limitations
    16  // under the License.
    17  
    18  package helper
    19  
    20  import (
    21  	"fmt"
    22  	"os"
    23  	"reflect"
    24  	"strconv"
    25  	"syscall"
    26  )
    27  
    28  type StateOS struct {
    29  	IdxHi uint64 `json:"idxhi,"`
    30  	IdxLo uint64 `json:"idxlo,"`
    31  	Vol   uint64 `json:"vol,"`
    32  	// TODO: Windows will not use them, so just add them to pass compilation.
    33  	ModifyTime uint64
    34  	Size       int64
    35  }
    36  
    37  // GetOSState returns the platform specific StateOS
    38  func GetOSState(info os.FileInfo) StateOS {
    39  	// os.SameFile must be called to populate the id fields. Otherwise in case for example
    40  	// os.Stat(file) is used to get the fileInfo, the ids are empty.
    41  	// https://github.com/elastic/beats/filebeat/pull/53
    42  	os.SameFile(info, info)
    43  
    44  	// Gathering fileStat (which is fileInfo) through reflection as otherwise not accessible
    45  	// See https://github.com/golang/go/blob/90c668d1afcb9a17ab9810bce9578eebade4db56/src/os/stat_windows.go#L33
    46  	fileStat := reflect.ValueOf(info).Elem()
    47  
    48  	// Get the three fields required to uniquely identify file und windows
    49  	// More details can be found here: https://msdn.microsoft.com/en-us/library/aa363788(v=vs.85).aspx
    50  	// Uint should already return uint64, but making sure this is the case
    51  	// The required fields can be found here: https://github.com/golang/go/blob/master/src/os/types_windows.go#L78
    52  	fileState := StateOS{
    53  		IdxHi:      uint64(fileStat.FieldByName("idxhi").Uint()),
    54  		IdxLo:      uint64(fileStat.FieldByName("idxlo").Uint()),
    55  		Vol:        uint64(fileStat.FieldByName("vol").Uint()),
    56  		ModifyTime: uint64(info.ModTime().UnixNano()),
    57  		Size:       info.Size(),
    58  	}
    59  
    60  	return fileState
    61  }
    62  
    63  // IsSame file checks if the files are identical
    64  func (fs StateOS) IsSame(state StateOS) bool {
    65  	return fs.IdxHi == state.IdxHi && fs.IdxLo == state.IdxLo && fs.Vol == state.Vol
    66  }
    67  
    68  func (fs StateOS) IsChange(state StateOS) bool {
    69  	if !fs.IsSame(state) {
    70  		return true
    71  	}
    72  	return fs.Size != state.Size || fs.ModifyTime != state.ModifyTime
    73  }
    74  
    75  func (fs StateOS) IsFileChange(state StateOS) bool {
    76  	return fs.IdxHi != state.IdxHi || fs.IdxLo != state.IdxLo || fs.Vol != state.Vol
    77  }
    78  
    79  func (fs *StateOS) IsEmpty() bool {
    80  	return fs.IdxHi == 0 && fs.IdxLo == 0 && fs.Vol == 0
    81  }
    82  
    83  func (fs StateOS) String() string {
    84  	var buf [130]byte
    85  	current := strconv.AppendUint(buf[:0], fs.IdxHi, 10)
    86  	current = append(current, '-')
    87  	current = strconv.AppendUint(current, fs.IdxLo, 10)
    88  	current = append(current, '-')
    89  	current = strconv.AppendUint(current, fs.Vol, 10)
    90  	current = append(current, '-')
    91  	current = strconv.AppendUint(current, fs.ModifyTime, 10)
    92  	current = append(current, '-')
    93  	current = strconv.AppendInt(current, fs.Size, 10)
    94  	return string(current)
    95  }
    96  
    97  // ReadOpen opens a file for reading only
    98  // As Windows blocks deleting a file when its open, some special params are passed here.
    99  func ReadOpen(path string) (*os.File, error) {
   100  	// Set all write flags
   101  	// This indirectly calls syscall_windows::Open method https://github.com/golang/go/blob/7ebcf5eac7047b1eef2443eda1786672b5c70f51/src/syscall/syscall_windows.go#L251
   102  	// As FILE_SHARE_DELETE cannot be passed to Open, os.CreateFile must be implemented directly
   103  
   104  	// This is mostly the code from syscall_windows::Open. Only difference is passing the Delete flag
   105  	// TODO: Open pull request to Golang so also Delete flag can be set
   106  	if len(path) == 0 {
   107  		return nil, fmt.Errorf("File '%s' not found. Error: %v", path, syscall.ERROR_FILE_NOT_FOUND)
   108  	}
   109  
   110  	pathp, err := syscall.UTF16PtrFromString(path)
   111  	if err != nil {
   112  		return nil, fmt.Errorf("Error converting to UTF16: %v", err)
   113  	}
   114  
   115  	var access uint32
   116  	access = syscall.GENERIC_READ
   117  
   118  	sharemode := uint32(syscall.FILE_SHARE_READ | syscall.FILE_SHARE_WRITE | syscall.FILE_SHARE_DELETE)
   119  
   120  	var sa *syscall.SecurityAttributes
   121  
   122  	var createmode uint32
   123  
   124  	createmode = syscall.OPEN_EXISTING
   125  
   126  	handle, err := syscall.CreateFile(pathp, access, sharemode, sa, createmode, syscall.FILE_ATTRIBUTE_NORMAL, 0)
   127  
   128  	if err != nil {
   129  		return nil, fmt.Errorf("Error creating file '%s': %v", path, err)
   130  	}
   131  
   132  	return os.NewFile(uintptr(handle), path), nil
   133  }