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 }