Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix Errno::EACCES which occurs while testing on Ruby 3.2 #3883

Merged
merged 8 commits into from
Sep 2, 2022
137 changes: 137 additions & 0 deletions lib/fluent/file_wrapper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,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
131 changes: 0 additions & 131 deletions lib/fluent/plugin/file_wrapper.rb

This file was deleted.

7 changes: 1 addition & 6 deletions lib/fluent/plugin/in_tail.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,7 @@
require 'fluent/capability'
require 'fluent/plugin/in_tail/position_file'
require 'fluent/plugin/in_tail/group_watch'

if Fluent.windows?
require_relative 'file_wrapper'
else
Fluent::FileWrapper = File
end
require 'fluent/file_wrapper'

module Fluent::Plugin
class TailInput < Fluent::Plugin::Input
Expand Down