{"record":{"id":"39020a2c0f817e85","repo":"puppetlabs/puppet","slug":"deviceiocontrol-handle-io-control-code-i","errorCode":null,"errorMessage":"DeviceIoControl(#{handle}, #{io_control_code}, #{in_buffer}, #{in_buffer ? in_buffer.size : ''}, #{out_buffer}, #{out_buffer ? out_buffer.size : ''})","messagePattern":"DeviceIoControl\\(#(.+?), #(.+?), #(.+?), #(.+?), #(.+?), #(.+?)\\)","errorType":"exception","errorClass":"Puppet::Util::Windows::Error","httpStatus":null,"severity":"error","filePath":"lib/puppet/util/windows/file.rb","lineNumber":197,"sourceCode":"  end\n\n  def self.device_io_control(handle, io_control_code, in_buffer = nil, out_buffer = nil)\n    if out_buffer.nil?\n      raise Puppet::Util::Windows::Error, _(\"out_buffer is required\")\n    end\n\n    FFI::MemoryPointer.new(:dword, 1) do |bytes_returned_ptr|\n      result = DeviceIoControl(\n        handle,\n        io_control_code,\n        in_buffer, in_buffer.nil? ? 0 : in_buffer.size,\n        out_buffer, out_buffer.size,\n        bytes_returned_ptr,\n        nil\n      )\n\n      if result == FFI::WIN32_FALSE\n        raise Puppet::Util::Windows::Error, \"DeviceIoControl(#{handle}, #{io_control_code}, \" \\\n                                            \"#{in_buffer}, #{in_buffer ? in_buffer.size : ''}, \" \\\n                                            \"#{out_buffer}, #{out_buffer ? out_buffer.size : ''}\"\n      end\n    end\n\n    out_buffer\n  end\n\n  def reparse_point?(file_name)\n    attributes = get_attributes(file_name, false)\n\n    return false if attributes == INVALID_FILE_ATTRIBUTES\n\n    (attributes & FILE_ATTRIBUTE_REPARSE_POINT) == FILE_ATTRIBUTE_REPARSE_POINT\n  end\n  module_function :reparse_point?\n\n  def symlink?(file_name)","sourceCodeStart":179,"sourceCodeEnd":215,"githubUrl":"https://github.com/puppetlabs/puppet/blob/e227c27540975c25aa22d533a52424a9d2fc886a/lib/puppet/util/windows/file.rb#L179-L215","documentation":"device_io_control raises Puppet::Util::Windows::Error when the DeviceIoControl Win32 call itself returns FALSE, echoing handle, control code, and buffer pointers/sizes into the message. The real reason is in e.code: for FSCTL_GET_REPARSE_POINT the classic values are ERROR_NOT_A_REPARSE_POINT (4390/0x1126) when the file has no reparse data, plus invalid-handle, access-denied, and insufficient-buffer variants.","triggerScenarios":"Sending FSCTL_GET_REPARSE_POINT to a handle opened on a plain (non-reparse) file (4390); using a stale or closed handle (6); a handle opened without FILE_FLAG_OPEN_REPARSE_POINT so the filter never sees the request; an out_buffer smaller than the reparse data requires (122, ERROR_INSUFFICIENT_BUFFER).","commonSituations":"Symlink-inspection code that assumes every file is a reparse point after a racy existence check; handles kept across I/O boundaries and invalidated by filter drivers; buffers sized below the documented 10240-byte minimum for reparse data.","solutions":["Gate on e.code: 4390 means 'not a reparse point' (skip, don't fail), 6 stale handle (reopen), 122 grow the buffer","Check reparse_point?(file_name) before opening/issuing FSCTL_GET_REPARSE_POINT","Open handles with FILE_FLAG_OPEN_REPARSE_POINT when inspecting links","Allocate reparse out_buffers >= MAXIMUM_REPARSE_DATA_BUFFER_SIZE (10240)"],"exampleFix":"// before\nPuppet::Util::Windows::File.device_io_control(handle, FSCTL_GET_REPARSE_POINT, nil, out)  # plain file -> 4390\n\n// after\nif Puppet::Util::Windows::File.reparse_point?(path)\n  Puppet::Util::Windows::File.device_io_control(handle, FSCTL_GET_REPARSE_POINT, nil, out)\nend","handlingStrategy":"try-catch","validationCode":"raise ArgumentError, 'not a reparse point' unless Puppet::Util::Windows::File.reparse_point?(path)","typeGuard":null,"tryCatchPattern":"begin\n  Puppet::Util::Windows::File.device_io_control(handle, FSCTL_GET_REPARSE_POINT, nil, out_buffer)\nrescue Puppet::Util::Windows::Error => e\n  raise unless e.code == 4390  # ERROR_NOT_A_REPARSE_POINT - benign probe miss\n  nil\nend","preventionTips":["Call reparse_point? before issuing FSCTL_GET_REPARSE_POINT","Open target files with FILE_FLAG_OPEN_REPARSE_POINT","Size out_buffer to at least MAXIMUM_REPARSE_DATA_BUFFER_SIZE (10240) to avoid code 122"],"tags":["windows","ffi","deviceiocontrol","reparse-point","win32-api","puppet"],"backgroundTag":"win32-device-control-failed","analyzedSha":"e227c27540975c25aa22d533a52424a9d2fc886a","analyzedAt":"2026-08-21T20:49:46.650Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}