forked from fluent/fluentd
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathfile_wrapper.rb
137 lines (120 loc) · 3.88 KB
/
file_wrapper.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
#
# Fluentd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
unless Fluent.windows?
Fluent::FileWrapper = File
else
require 'fluent/win32api'
module Fluent
module FileWrapper
def self.open(path, mode='r')
io = WindowsFile.new(path, mode).io
if block_given?
v = yield io
io.close
v
else
io
end
end
def self.stat(path)
f = WindowsFile.new(path)
s = f.stat
f.close
s
end
end
class WindowsFile
include File::Constants
attr_reader :io
INVALID_HANDLE_VALUE = -1
def initialize(path, mode_enc='r')
@path = path
mode, enc = mode_enc.split(":", 2)
@io = File.open(path, mode2flags(mode))
@io.set_encoding(enc) if enc
@file_handle = Win32API._get_osfhandle(@io.to_i)
@io.instance_variable_set(:@file_index, self.ino)
def @io.ino
@file_index
end
end
def close
@io.close
@file_handle = INVALID_HANDLE_VALUE
end
# To keep backward compatibility, we continue to use GetFileInformationByHandle()
# to get file id.
# Note that Ruby's File.stat uses GetFileInformationByHandleEx() with FileIdInfo
# and returned value is different with above one, former one is 64 bit while
# later one is 128bit.
def ino
by_handle_file_information = '\0'*(4+8+8+8+4+4+4+4+4+4) #72bytes
unless Win32API.GetFileInformationByHandle(@file_handle, by_handle_file_information)
return 0
end
by_handle_file_information.unpack("I11Q1")[11] # fileindex
end
def stat
raise Errno::ENOENT if delete_pending
s = File.stat(@path)
s.instance_variable_set :@ino, self.ino
def s.ino; @ino; end
s
end
private
def mode2flags(mode)
# Always inject File::Constants::SHARE_DELETE
# https://github.com/fluent/fluentd/pull/3585#issuecomment-1101502617
# To enable SHARE_DELETE, BINARY is also required.
# https://bugs.ruby-lang.org/issues/11218
# https://github.com/ruby/ruby/blob/d6684f063bc53e3cab025bd39526eca3b480b5e7/win32/win32.c#L6332-L6345
flags = BINARY | SHARE_DELETE
case mode.delete("b")
when "r"
flags |= RDONLY
when "r+"
flags |= RDWR
when "w"
flags |= WRONLY | CREAT | TRUNC
when "w+"
flags |= RDWR | CREAT | TRUNC
when "a"
flags |= WRONLY | CREAT | APPEND
when "a+"
flags |= RDWR | CREAT | APPEND
else
raise Errno::EINVAL.new("Unsupported mode by Fluent::FileWrapper: #{mode}")
end
end
# DeletePending is a Windows-specific file state that roughly means
# "this file is queued for deletion, so close any open handlers"
#
# This flag can be retrieved via GetFileInformationByHandleEx().
#
# https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-getfileinformationbyhandleex
#
def delete_pending
file_standard_info = 0x01
bufsize = 1024
buf = '\0' * bufsize
unless Win32API.GetFileInformationByHandleEx(@file_handle, file_standard_info, buf, bufsize)
return false
end
return buf.unpack("QQICC")[3] != 0
end
end
end
end