vagrant-unbundled 2.0.3.0 → 2.0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (164) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +43 -10
  3. data/Gemfile.lock +16 -2
  4. data/README.md +1 -0
  5. data/bin/vagrant +1 -1
  6. data/lib/vagrant/action/builtin/handle_box.rb +1 -1
  7. data/lib/vagrant/action/general/package.rb +1 -1
  8. data/lib/vagrant/action.rb +3 -1
  9. data/lib/vagrant/alias.rb +56 -0
  10. data/lib/vagrant/bundler.rb +1 -1
  11. data/lib/vagrant/capability_host.rb +1 -1
  12. data/lib/vagrant/cli.rb +8 -0
  13. data/lib/vagrant/config/version_base.rb +1 -1
  14. data/lib/vagrant/environment.rb +7 -1
  15. data/lib/vagrant/errors.rb +4 -0
  16. data/lib/vagrant/host.rb +1 -1
  17. data/lib/vagrant/machine.rb +1 -1
  18. data/lib/vagrant/machine_index.rb +1 -1
  19. data/lib/vagrant/plugin/v1/guest.rb +1 -1
  20. data/lib/vagrant/plugin/v1/host.rb +1 -1
  21. data/lib/vagrant/plugin/v1/plugin.rb +1 -1
  22. data/lib/vagrant/plugin/v1/provider.rb +1 -1
  23. data/lib/vagrant/plugin/v2/guest.rb +1 -1
  24. data/lib/vagrant/plugin/v2/host.rb +1 -1
  25. data/lib/vagrant/plugin/v2/plugin.rb +1 -1
  26. data/lib/vagrant/plugin/v2/provider.rb +1 -1
  27. data/lib/vagrant/shared_helpers.rb +30 -9
  28. data/lib/vagrant/util/checkpoint_client.rb +8 -0
  29. data/lib/vagrant/util/downloader.rb +1 -1
  30. data/lib/vagrant/util/platform.rb +11 -3
  31. data/lib/vagrant/util/powershell.rb +17 -1
  32. data/lib/vagrant/util/safe_exec.rb +1 -1
  33. data/lib/vagrant/util/ssh.rb +4 -1
  34. data/lib/vagrant/util/stacked_proc_runner.rb +1 -1
  35. data/lib/vagrant/util/template_renderer.rb +2 -2
  36. data/lib/vagrant/vagrantfile.rb +1 -1
  37. data/lib/vagrant.rb +8 -0
  38. data/plugins/commands/plugin/gem_helper.rb +1 -1
  39. data/plugins/commands/powershell/command.rb +2 -2
  40. data/plugins/commands/powershell/scripts/enable_psremoting.ps1 +1 -1
  41. data/plugins/communicators/ssh/communicator.rb +10 -1
  42. data/plugins/communicators/winrm/command_filters/mkdir.rb +4 -2
  43. data/plugins/communicators/winrm/command_filters/rm.rb +5 -3
  44. data/plugins/communicators/winrm/command_filters/test.rb +3 -1
  45. data/plugins/communicators/winrm/command_filters/which.rb +4 -2
  46. data/plugins/communicators/winrm/communicator.rb +3 -3
  47. data/plugins/guests/amazon/cap/flavor.rb +1 -1
  48. data/plugins/guests/coreos/cap/configure_networks.rb +1 -1
  49. data/plugins/guests/coreos/guest.rb +1 -1
  50. data/plugins/guests/debian/cap/configure_networks.rb +1 -1
  51. data/plugins/guests/freebsd/cap/configure_networks.rb +1 -1
  52. data/plugins/guests/photon/guest.rb +1 -1
  53. data/plugins/guests/windows/cap/change_host_name.rb +6 -1
  54. data/plugins/guests/windows/cap/mount_shared_folder.rb +1 -1
  55. data/plugins/hosts/alt/plugin.rb +1 -1
  56. data/plugins/hosts/arch/plugin.rb +1 -1
  57. data/plugins/hosts/gentoo/plugin.rb +1 -1
  58. data/plugins/hosts/linux/plugin.rb +1 -1
  59. data/plugins/hosts/redhat/plugin.rb +1 -1
  60. data/plugins/hosts/slackware/plugin.rb +2 -2
  61. data/plugins/kernel_v2/config/push.rb +1 -1
  62. data/plugins/kernel_v2/config/vm.rb +1 -1
  63. data/plugins/providers/docker/action/compare_synced_folders.rb +1 -1
  64. data/plugins/providers/virtualbox/action/network.rb +9 -5
  65. data/plugins/providers/virtualbox/action/network_fix_ipv6.rb +1 -1
  66. data/plugins/provisioners/chef/config/chef_zero.rb +1 -1
  67. data/plugins/provisioners/chef/provisioner/chef_solo.rb +2 -2
  68. data/plugins/provisioners/chef/provisioner/chef_zero.rb +3 -3
  69. data/plugins/provisioners/puppet/config/puppet.rb +2 -0
  70. data/plugins/provisioners/puppet/provisioner/puppet.rb +18 -1
  71. data/plugins/provisioners/salt/config.rb +6 -0
  72. data/plugins/provisioners/salt/provisioner.rb +11 -1
  73. data/templates/locales/command_ps.yml +1 -1
  74. data/templates/locales/en.yml +13 -4
  75. data/templates/locales/guest_windows.yml +1 -1
  76. data/templates/locales/providers_docker.yml +1 -1
  77. data/templates/locales/synced_folder_smb.yml +2 -2
  78. data/vagrant.gemspec +2 -0
  79. data/vendor/bundle/ruby/2.5.0/bundler/gems/vagrant-spec-f3daedaac493/vagrant-spec.gemspec +1 -1
  80. data/vendor/bundle/ruby/2.5.0/gems/domain_name-0.5.20180417/.document +5 -0
  81. data/vendor/bundle/ruby/2.5.0/gems/domain_name-0.5.20180417/.gitignore +17 -0
  82. data/vendor/bundle/ruby/2.5.0/gems/domain_name-0.5.20180417/.travis.yml +21 -0
  83. data/vendor/bundle/ruby/2.5.0/gems/domain_name-0.5.20180417/CHANGELOG.md +209 -0
  84. data/vendor/bundle/ruby/2.5.0/gems/domain_name-0.5.20180417/Gemfile +4 -0
  85. data/vendor/bundle/ruby/2.5.0/gems/domain_name-0.5.20180417/LICENSE.txt +78 -0
  86. data/vendor/bundle/ruby/2.5.0/gems/domain_name-0.5.20180417/README.md +67 -0
  87. data/vendor/bundle/ruby/2.5.0/gems/domain_name-0.5.20180417/Rakefile +88 -0
  88. data/vendor/bundle/ruby/2.5.0/gems/domain_name-0.5.20180417/data/public_suffix_list.dat +12440 -0
  89. data/vendor/bundle/ruby/2.5.0/gems/domain_name-0.5.20180417/domain_name.gemspec +36 -0
  90. data/vendor/bundle/ruby/2.5.0/gems/domain_name-0.5.20180417/lib/domain_name/etld_data.rb +8474 -0
  91. data/vendor/bundle/ruby/2.5.0/gems/domain_name-0.5.20180417/lib/domain_name/etld_data.rb.erb +11 -0
  92. data/vendor/bundle/ruby/2.5.0/gems/domain_name-0.5.20180417/lib/domain_name/punycode.rb +283 -0
  93. data/vendor/bundle/ruby/2.5.0/gems/domain_name-0.5.20180417/lib/domain_name/version.rb +3 -0
  94. data/vendor/bundle/ruby/2.5.0/gems/domain_name-0.5.20180417/lib/domain_name.rb +297 -0
  95. data/vendor/bundle/ruby/2.5.0/gems/domain_name-0.5.20180417/test/helper.rb +17 -0
  96. data/vendor/bundle/ruby/2.5.0/gems/domain_name-0.5.20180417/test/test_domain_name-punycode.rb +97 -0
  97. data/vendor/bundle/ruby/2.5.0/gems/domain_name-0.5.20180417/test/test_domain_name.rb +317 -0
  98. data/vendor/bundle/ruby/2.5.0/gems/domain_name-0.5.20180417/tool/gen_etld_data.rb +63 -0
  99. data/vendor/bundle/ruby/2.5.0/gems/ffi-win32-extensions-1.0.3/CHANGES +13 -0
  100. data/vendor/bundle/ruby/2.5.0/gems/ffi-win32-extensions-1.0.3/LICENSE +201 -0
  101. data/vendor/bundle/ruby/2.5.0/gems/ffi-win32-extensions-1.0.3/MANIFEST +9 -0
  102. data/vendor/bundle/ruby/2.5.0/gems/ffi-win32-extensions-1.0.3/README +48 -0
  103. data/vendor/bundle/ruby/2.5.0/gems/ffi-win32-extensions-1.0.3/Rakefile +42 -0
  104. data/vendor/bundle/ruby/2.5.0/gems/ffi-win32-extensions-1.0.3/certs/djberg96_pub.pem +21 -0
  105. data/vendor/bundle/ruby/2.5.0/gems/ffi-win32-extensions-1.0.3/ffi-win32-extensions.gemspec +25 -0
  106. data/vendor/bundle/ruby/2.5.0/gems/ffi-win32-extensions-1.0.3/lib/ffi/win32/extensions.rb +96 -0
  107. data/vendor/bundle/ruby/2.5.0/gems/ffi-win32-extensions-1.0.3/lib/ffi-win32-extensions.rb +1 -0
  108. data/vendor/bundle/ruby/2.5.0/gems/ffi-win32-extensions-1.0.3/test/test_ffi_extensions.rb +41 -0
  109. data/vendor/bundle/ruby/2.5.0/gems/ffi-win32-extensions-1.0.3/test/test_string_extensions.rb +20 -0
  110. data/vendor/bundle/ruby/2.5.0/gems/win32-file-0.8.1/CHANGES +235 -0
  111. data/vendor/bundle/ruby/2.5.0/gems/win32-file-0.8.1/MANIFEST +16 -0
  112. data/vendor/bundle/ruby/2.5.0/gems/win32-file-0.8.1/README +73 -0
  113. data/vendor/bundle/ruby/2.5.0/gems/win32-file-0.8.1/Rakefile +49 -0
  114. data/vendor/bundle/ruby/2.5.0/gems/win32-file-0.8.1/appveyor.yml +50 -0
  115. data/vendor/bundle/ruby/2.5.0/gems/win32-file-0.8.1/certs/djberg96_pub.pem +21 -0
  116. data/vendor/bundle/ruby/2.5.0/gems/win32-file-0.8.1/lib/win32/file/constants.rb +33 -0
  117. data/vendor/bundle/ruby/2.5.0/gems/win32-file-0.8.1/lib/win32/file/functions.rb +42 -0
  118. data/vendor/bundle/ruby/2.5.0/gems/win32-file-0.8.1/lib/win32/file/structs.rb +54 -0
  119. data/vendor/bundle/ruby/2.5.0/gems/win32-file-0.8.1/lib/win32/file.rb +585 -0
  120. data/vendor/bundle/ruby/2.5.0/gems/win32-file-0.8.1/lib/win32-file.rb +1 -0
  121. data/vendor/bundle/ruby/2.5.0/gems/win32-file-0.8.1/test/test_win32_file_link.rb +141 -0
  122. data/vendor/bundle/ruby/2.5.0/gems/win32-file-0.8.1/test/test_win32_file_misc.rb +16 -0
  123. data/vendor/bundle/ruby/2.5.0/gems/win32-file-0.8.1/test/test_win32_file_path.rb +282 -0
  124. data/vendor/bundle/ruby/2.5.0/gems/win32-file-0.8.1/test/test_win32_file_stat.rb +330 -0
  125. data/vendor/bundle/ruby/2.5.0/gems/win32-file-0.8.1/win32-file.gemspec +33 -0
  126. data/vendor/bundle/ruby/2.5.0/gems/win32-file-security-1.0.10/CHANGES +63 -0
  127. data/vendor/bundle/ruby/2.5.0/gems/win32-file-security-1.0.10/MANIFEST +18 -0
  128. data/vendor/bundle/ruby/2.5.0/gems/win32-file-security-1.0.10/README +47 -0
  129. data/vendor/bundle/ruby/2.5.0/gems/win32-file-security-1.0.10/Rakefile +66 -0
  130. data/vendor/bundle/ruby/2.5.0/gems/win32-file-security-1.0.10/appveyor.yml +53 -0
  131. data/vendor/bundle/ruby/2.5.0/gems/win32-file-security-1.0.10/certs/djberg96_pub.pem +21 -0
  132. data/vendor/bundle/ruby/2.5.0/gems/win32-file-security-1.0.10/lib/win32/file/security/constants.rb +149 -0
  133. data/vendor/bundle/ruby/2.5.0/gems/win32-file-security-1.0.10/lib/win32/file/security/functions.rb +63 -0
  134. data/vendor/bundle/ruby/2.5.0/gems/win32-file-security-1.0.10/lib/win32/file/security/structs.rb +68 -0
  135. data/vendor/bundle/ruby/2.5.0/gems/win32-file-security-1.0.10/lib/win32/file/security.rb +963 -0
  136. data/vendor/bundle/ruby/2.5.0/gems/win32-file-security-1.0.10/lib/win32-file-security.rb +1 -0
  137. data/vendor/bundle/ruby/2.5.0/gems/win32-file-security-1.0.10/test/test_win32_file_security_acls.rb +34 -0
  138. data/vendor/bundle/ruby/2.5.0/gems/win32-file-security-1.0.10/test/test_win32_file_security_constants.rb +54 -0
  139. data/vendor/bundle/ruby/2.5.0/gems/win32-file-security-1.0.10/test/test_win32_file_security_encryption.rb +90 -0
  140. data/vendor/bundle/ruby/2.5.0/gems/win32-file-security-1.0.10/test/test_win32_file_security_ffi.rb +33 -0
  141. data/vendor/bundle/ruby/2.5.0/gems/win32-file-security-1.0.10/test/test_win32_file_security_ownership.rb +174 -0
  142. data/vendor/bundle/ruby/2.5.0/gems/win32-file-security-1.0.10/test/test_win32_file_security_permissions.rb +88 -0
  143. data/vendor/bundle/ruby/2.5.0/gems/win32-file-security-1.0.10/test/test_win32_file_security_version.rb +14 -0
  144. data/vendor/bundle/ruby/2.5.0/gems/win32-file-security-1.0.10/win32-file-security.gemspec +28 -0
  145. data/vendor/bundle/ruby/2.5.0/gems/win32-file-stat-1.5.5/CHANGES +169 -0
  146. data/vendor/bundle/ruby/2.5.0/gems/win32-file-stat-1.5.5/MANIFEST +12 -0
  147. data/vendor/bundle/ruby/2.5.0/gems/win32-file-stat-1.5.5/README +94 -0
  148. data/vendor/bundle/ruby/2.5.0/gems/win32-file-stat-1.5.5/Rakefile +28 -0
  149. data/vendor/bundle/ruby/2.5.0/gems/win32-file-stat-1.5.5/appveyor.yml +48 -0
  150. data/vendor/bundle/ruby/2.5.0/gems/win32-file-stat-1.5.5/certs/djberg96_pub.pem +21 -0
  151. data/vendor/bundle/ruby/2.5.0/gems/win32-file-stat-1.5.5/lib/win32/file/stat.rb +1008 -0
  152. data/vendor/bundle/ruby/2.5.0/gems/win32-file-stat-1.5.5/lib/win32/file/windows/constants.rb +94 -0
  153. data/vendor/bundle/ruby/2.5.0/gems/win32-file-stat-1.5.5/lib/win32/file/windows/functions.rb +68 -0
  154. data/vendor/bundle/ruby/2.5.0/gems/win32-file-stat-1.5.5/lib/win32/file/windows/structs.rb +196 -0
  155. data/vendor/bundle/ruby/2.5.0/gems/win32-file-stat-1.5.5/lib/win32-file-stat.rb +1 -0
  156. data/vendor/bundle/ruby/2.5.0/gems/win32-file-stat-1.5.5/test/test_file_stat.rb +677 -0
  157. data/vendor/bundle/ruby/2.5.0/gems/win32-file-stat-1.5.5/win32-file-stat.gemspec +30 -0
  158. data/vendor/bundle/ruby/2.5.0/specifications/domain_name-0.5.20180417.gemspec +46 -0
  159. data/vendor/bundle/ruby/2.5.0/specifications/ffi-win32-extensions-1.0.3.gemspec +38 -0
  160. data/vendor/bundle/ruby/2.5.0/specifications/win32-file-0.8.1.gemspec +51 -0
  161. data/vendor/bundle/ruby/2.5.0/specifications/win32-file-security-1.0.10.gemspec +47 -0
  162. data/vendor/bundle/ruby/2.5.0/specifications/win32-file-stat-1.5.5.gemspec +48 -0
  163. data/version.txt +1 -1
  164. metadata +114 -2
@@ -0,0 +1,963 @@
1
+ require_relative 'security/constants'
2
+ require_relative 'security/structs'
3
+ require_relative 'security/functions'
4
+ require 'socket'
5
+
6
+ class File
7
+ include Windows::File::Constants
8
+ include Windows::File::Functions
9
+ extend Windows::File::Constants
10
+ extend Windows::File::Structs
11
+ extend Windows::File::Functions
12
+
13
+ # The version of the win32-file library
14
+ WIN32_FILE_SECURITY_VERSION = '1.0.10'.freeze
15
+
16
+ class << self
17
+ remove_method(:chown)
18
+ remove_method(:grpowned?)
19
+ remove_method(:owned?)
20
+
21
+ # Returns the encryption status of a file as a string. Possible return
22
+ # values are:
23
+ #
24
+ # * encryptable
25
+ # * encrypted
26
+ # * readonly
27
+ # * root directory (i.e. not encryptable)
28
+ # * system file (i.e. not encryptable)
29
+ # * unsupported
30
+ # * unknown
31
+ #
32
+ def encryption_status(file)
33
+ wide_file = string_check(file).wincode
34
+ status_ptr = FFI::MemoryPointer.new(:ulong)
35
+
36
+ unless FileEncryptionStatusW(wide_file, status_ptr)
37
+ raise SystemCallError.new("FileEncryptionStatus", FFI.errno)
38
+ end
39
+
40
+ status = status_ptr.read_ulong
41
+
42
+ rvalue = case status
43
+ when FILE_ENCRYPTABLE
44
+ "encryptable"
45
+ when FILE_IS_ENCRYPTED
46
+ "encrypted"
47
+ when FILE_READ_ONLY
48
+ "readonly"
49
+ when FILE_ROOT_DIR
50
+ "root directory"
51
+ when FILE_SYSTEM_ATTR
52
+ "system file"
53
+ when FILE_SYSTEM_NOT_SUPPORTED
54
+ "unsupported"
55
+ else
56
+ "unknown"
57
+ end
58
+
59
+ rvalue
60
+ end
61
+
62
+ # Returns whether or not the root path of the specified file is
63
+ # encryptable. If no path or a relative path is specified, it will
64
+ # check against the root of the current directory.
65
+ #
66
+ # Be sure to include a trailing slash if specifying a root path.
67
+ #
68
+ # Examples:
69
+ #
70
+ # p File.encryptable?
71
+ # p File.encryptable?("D:/")
72
+ # p File.encryptable?("C:/foo/bar.txt") # Same as 'C:\'
73
+ #
74
+ def encryptable?(file = nil)
75
+ bool = false
76
+ flags_ptr = FFI::MemoryPointer.new(:ulong)
77
+
78
+ if file
79
+ file = File.expand_path(string_check(file))
80
+ wide_file = file.wincode
81
+
82
+ if !PathIsRootW(wide_file)
83
+ unless PathStripToRootW(wide_file)
84
+ raise SystemCallError.new("PathStripToRoot", FFI.errno)
85
+ end
86
+ end
87
+ else
88
+ wide_file = nil
89
+ end
90
+
91
+ unless GetVolumeInformationW(wide_file, nil, 0, nil, nil, flags_ptr, nil, 0)
92
+ raise SystemCallError.new("GetVolumeInformation", FFI.errno)
93
+ end
94
+
95
+ flags = flags_ptr.read_ulong
96
+
97
+ if flags & FILE_SUPPORTS_ENCRYPTION > 0
98
+ bool = true
99
+ end
100
+
101
+ bool
102
+ end
103
+
104
+
105
+ # Returns whether or not the root path of the specified file supports
106
+ # ACL's. If no path or a relative path is specified, it will check against
107
+ # the root of the current directory.
108
+ #
109
+ # Be sure to include a trailing slash if specifying a root path.
110
+ #
111
+ # Examples:
112
+ #
113
+ # p File.supports_acls?
114
+ # p File.supports_acls?("D:/")
115
+ # p File.supports_acls?("C:/foo/bar.txt") # Same as 'C:\'
116
+ #
117
+ def supports_acls?(file = nil)
118
+ bool = false
119
+ flags_ptr = FFI::MemoryPointer.new(:ulong)
120
+
121
+ if file
122
+ file = File.expand_path(string_check(file))
123
+ wide_file = file.wincode
124
+
125
+ if !PathIsRootW(wide_file)
126
+ unless PathStripToRootW(wide_file)
127
+ raise SystemCallError.new("PathStripToRoot", FFI.errno)
128
+ end
129
+ end
130
+ else
131
+ wide_file = nil
132
+ end
133
+
134
+ unless GetVolumeInformationW(wide_file, nil, 0, nil, nil, flags_ptr, nil, 0)
135
+ raise SystemCallError.new("GetVolumeInformation", FFI.errno)
136
+ end
137
+
138
+ flags = flags_ptr.read_ulong
139
+
140
+ if flags & FILE_PERSISTENT_ACLS > 0
141
+ bool = true
142
+ end
143
+
144
+ bool
145
+ end
146
+
147
+ # Encrypts a file or directory. All data streams in a file are encrypted.
148
+ # All new files created in an encrypted directory are encrypted.
149
+ #
150
+ # The caller must have the FILE_READ_DATA, FILE_WRITE_DATA,
151
+ # FILE_READ_ATTRIBUTES, FILE_WRITE_ATTRIBUTES, and SYNCHRONIZE access
152
+ # rights.
153
+ #
154
+ # Requires exclusive access to the file being encrypted, and will fail if
155
+ # another process is using the file or the file is marked read-only. If the
156
+ # file is compressed the file will be decompressed before encrypting it.
157
+ #
158
+ def encrypt(file)
159
+ unless EncryptFileW(string_check(file).wincode)
160
+ raise SystemCallError.new("EncryptFile", FFI.errno)
161
+ end
162
+ self
163
+ end
164
+
165
+ # Decrypts an encrypted file or directory.
166
+ #
167
+ # The caller must have the FILE_READ_DATA, FILE_WRITE_DATA,
168
+ # FILE_READ_ATTRIBUTES, FILE_WRITE_ATTRIBUTES, and SYNCHRONIZE access
169
+ # rights.
170
+ #
171
+ # Requires exclusive access to the file being decrypted, and will fail if
172
+ # another process is using the file. If the file is not encrypted an error
173
+ # is NOT raised, it's simply a no-op.
174
+ #
175
+ def decrypt(file)
176
+ unless DecryptFileW(string_check(file).wincode, 0)
177
+ raise SystemCallError.new("DecryptFile", FFI.errno)
178
+ end
179
+ self
180
+ end
181
+
182
+ # Returns a hash describing the current file permissions for the given
183
+ # file. The account name is the key, and the value is an integer mask
184
+ # that corresponds to the security permissions for that file.
185
+ #
186
+ # To get a human readable version of the permissions, pass the value to
187
+ # the +File.securities+ method.
188
+ #
189
+ # You may optionally specify a host as the second argument. If no host is
190
+ # specified then the current host is used.
191
+ #
192
+ # Examples:
193
+ #
194
+ # hash = File.get_permissions('test.txt')
195
+ #
196
+ # p hash # => {"NT AUTHORITY\\SYSTEM"=>2032127, "BUILTIN\\Administrators"=>2032127, ...}
197
+ #
198
+ # hash.each{ |name, mask|
199
+ # p name
200
+ # p File.securities(mask)
201
+ # }
202
+ #
203
+ def get_permissions(file, host=nil)
204
+ # Check local filesystem to see if it supports ACL's. If not, bail early
205
+ # because the GetFileSecurity function will not fail on an unsupported FS.
206
+ if host.nil?
207
+ unless supports_acls?(file)
208
+ raise ArgumentError, "Filesystem does not implement ACL support"
209
+ end
210
+ end
211
+
212
+ wide_file = string_check(file).wincode
213
+ wide_host = host ? host.wincode : nil
214
+
215
+ size_needed_ptr = FFI::MemoryPointer.new(:ulong)
216
+ security_ptr = FFI::MemoryPointer.new(:ulong)
217
+
218
+ # First pass, get the size needed
219
+ bool = GetFileSecurityW(
220
+ wide_file,
221
+ DACL_SECURITY_INFORMATION,
222
+ security_ptr,
223
+ security_ptr.size,
224
+ size_needed_ptr
225
+ )
226
+
227
+ errno = FFI.errno
228
+
229
+ if !bool && errno != ERROR_INSUFFICIENT_BUFFER
230
+ raise SystemCallError.new("GetFileSecurity", errno)
231
+ end
232
+
233
+ size_needed = size_needed_ptr.read_ulong
234
+
235
+ security_ptr = FFI::MemoryPointer.new(size_needed)
236
+
237
+ # Second pass, this time with the appropriately sized security pointer
238
+ bool = GetFileSecurityW(
239
+ wide_file,
240
+ DACL_SECURITY_INFORMATION,
241
+ security_ptr,
242
+ security_ptr.size,
243
+ size_needed_ptr
244
+ )
245
+
246
+ raise SystemCallError.new("GetFileSecurity", FFI.errno) unless bool
247
+
248
+ control_ptr = FFI::MemoryPointer.new(:ulong)
249
+ revision_ptr = FFI::MemoryPointer.new(:ulong)
250
+
251
+ unless GetSecurityDescriptorControl(security_ptr, control_ptr, revision_ptr)
252
+ raise SystemCallError.new("GetSecurityDescriptorControl", FFI.errno)
253
+ end
254
+
255
+ control = control_ptr.read_ulong
256
+
257
+ if control & SE_DACL_PRESENT == 0
258
+ raise ArgumentError, "No DACL present: explicit deny all"
259
+ end
260
+
261
+ dacl_pptr = FFI::MemoryPointer.new(:pointer)
262
+ dacl_present_ptr = FFI::MemoryPointer.new(:bool)
263
+ dacl_defaulted_ptr = FFI::MemoryPointer.new(:ulong)
264
+
265
+ bool = GetSecurityDescriptorDacl(
266
+ security_ptr,
267
+ dacl_present_ptr,
268
+ dacl_pptr,
269
+ dacl_defaulted_ptr
270
+ )
271
+
272
+ unless bool
273
+ raise SystemCallError.new("GetSecurityDescriptorDacl", FFI.errno)
274
+ end
275
+
276
+ acl = ACL.new(dacl_pptr.read_pointer)
277
+
278
+ if acl[:AclRevision] == 0
279
+ raise ArgumentError, "DACL is NULL: implicit access grant"
280
+ end
281
+
282
+ ace_count = acl[:AceCount]
283
+ perms_hash = {}
284
+
285
+ 0.upto(ace_count - 1){ |i|
286
+ ace_pptr = FFI::MemoryPointer.new(:pointer)
287
+ next unless GetAce(acl, i, ace_pptr)
288
+
289
+ access = ACCESS_ALLOWED_ACE.new(ace_pptr.read_pointer)
290
+
291
+ if access[:Header][:AceType] == ACCESS_ALLOWED_ACE_TYPE
292
+ name = FFI::MemoryPointer.new(:uchar, 260)
293
+ name_size = FFI::MemoryPointer.new(:ulong)
294
+ name_size.write_ulong(name.size)
295
+
296
+ domain = FFI::MemoryPointer.new(:uchar, 260)
297
+ domain_size = FFI::MemoryPointer.new(:ulong)
298
+ domain_size.write_ulong(domain.size)
299
+
300
+ use_ptr = FFI::MemoryPointer.new(:pointer)
301
+
302
+ bool = LookupAccountSidW(
303
+ wide_host,
304
+ ace_pptr.read_pointer + 8,
305
+ name,
306
+ name_size,
307
+ domain,
308
+ domain_size,
309
+ use_ptr
310
+ )
311
+
312
+ raise SystemCallError.new("LookupAccountSid", FFI.errno) unless bool
313
+
314
+ # The x2 multiplier is necessary due to wide char strings.
315
+ name = name.read_string(name_size.read_ulong * 2).wstrip
316
+
317
+ dsize = domain_size.read_ulong
318
+
319
+ # It's possible there is no domain.
320
+ if dsize > 0
321
+ domain = domain.read_string(dsize * 2).wstrip
322
+
323
+ unless domain.empty?
324
+ name = domain + '\\' + name
325
+ end
326
+ end
327
+
328
+ perms_hash[name] = access[:Mask]
329
+ end
330
+ }
331
+
332
+ perms_hash
333
+ end
334
+
335
+ # Sets the file permissions for the given file name. The 'permissions'
336
+ # argument is a hash with an account name as the key, and the various
337
+ # permission constants as possible values. The possible constant values
338
+ # are:
339
+ #
340
+ # * FILE_READ_DATA
341
+ # * FILE_WRITE_DATA
342
+ # * FILE_APPEND_DATA
343
+ # * FILE_READ_EA
344
+ # * FILE_WRITE_EA
345
+ # * FILE_EXECUTE
346
+ # * FILE_DELETE_CHILD
347
+ # * FILE_READ_ATTRIBUTES
348
+ # * FILE_WRITE_ATTRIBUTES
349
+ # * FULL
350
+ # * READ
351
+ # * ADD
352
+ # * CHANGE
353
+ # * DELETE
354
+ # * READ_CONTROL
355
+ # * WRITE_DAC
356
+ # * WRITE_OWNER
357
+ # * SYNCHRONIZE
358
+ # * STANDARD_RIGHTS_ALL
359
+ # * STANDARD_RIGHTS_REQUIRED
360
+ # * STANDARD_RIGHTS_READ
361
+ # * STANDARD_RIGHTS_WRITE
362
+ # * STANDARD_RIGHTS_EXECUTE
363
+ # * SPECIFIC_RIGHTS_ALL
364
+ # * ACCESS_SYSTEM_SECURITY
365
+ # * MAXIMUM_ALLOWED
366
+ # * GENERIC_READ
367
+ # * GENERIC_WRITE
368
+ # * GENERIC_EXECUTE
369
+ # * GENERIC_ALL
370
+ #
371
+ # Example:
372
+ #
373
+ # # Set locally
374
+ # File.set_permissions(file, "userid" => File::GENERIC_ALL)
375
+ #
376
+ # # Set a remote system
377
+ # File.set_permissions(file, "host\\userid" => File::GENERIC_ALL)
378
+ #
379
+ def set_permissions(file, perms)
380
+ # Check local filesystem to see if it supports ACL's. If not, bail early
381
+ # because the GetFileSecurity function will not fail on an unsupported FS.
382
+ unless supports_acls?(file)
383
+ raise ArgumentError, "Filesystem does not implement ACL support"
384
+ end
385
+
386
+ wide_file = string_check(file).wincode
387
+ raise TypeError unless perms.kind_of?(Hash)
388
+
389
+ sec_desc = FFI::MemoryPointer.new(:pointer, SECURITY_DESCRIPTOR_MIN_LENGTH)
390
+
391
+ unless InitializeSecurityDescriptor(sec_desc, 1)
392
+ raise SystemCallError.new("InitializeSecurityDescriptor", FFI.errno)
393
+ end
394
+
395
+ acl_new = FFI::MemoryPointer.new(ACL, 100)
396
+
397
+ unless InitializeAcl(acl_new, acl_new.size, ACL_REVISION2)
398
+ raise SystemCallError.new("InitializeAcl", FFI.errno)
399
+ end
400
+
401
+ perms.each{ |account, mask|
402
+ next if mask.nil?
403
+
404
+ # reset account_rights for each entry in perms:
405
+ account_rights = 0
406
+
407
+ server, account = account.split("\\")
408
+
409
+ if ['BUILTIN', 'NT AUTHORITY'].include?(server.upcase)
410
+ wide_server = nil
411
+ elsif account.nil?
412
+ wide_server = nil
413
+ account = server
414
+ else
415
+ wide_server = server.wincode
416
+ end
417
+
418
+ wide_account = account.wincode
419
+
420
+ sid = FFI::MemoryPointer.new(:uchar, 1024)
421
+ sid_size = FFI::MemoryPointer.new(:ulong)
422
+ sid_size.write_ulong(sid.size)
423
+
424
+ domain = FFI::MemoryPointer.new(:uchar, 260)
425
+ domain_size = FFI::MemoryPointer.new(:ulong)
426
+ domain_size.write_ulong(domain.size)
427
+
428
+ use_ptr = FFI::MemoryPointer.new(:ulong)
429
+
430
+ val = LookupAccountNameW(
431
+ wide_server,
432
+ wide_account,
433
+ sid,
434
+ sid_size,
435
+ domain,
436
+ domain_size,
437
+ use_ptr
438
+ )
439
+
440
+ raise SystemCallError.new("LookupAccountName", FFI.errno) unless val
441
+
442
+ all_ace = ACCESS_ALLOWED_ACE2.new
443
+
444
+ val = CopySid(
445
+ ALLOW_ACE_LENGTH - ACCESS_ALLOWED_ACE.size,
446
+ all_ace.to_ptr+8,
447
+ sid
448
+ )
449
+
450
+ raise SystemCallError.new("CopySid", FFI.errno) unless val
451
+
452
+ if (GENERIC_ALL & mask).nonzero?
453
+ account_rights = GENERIC_ALL & mask
454
+ elsif (GENERIC_RIGHTS_CHK & mask).nonzero?
455
+ account_rights = GENERIC_RIGHTS_MASK & mask
456
+ else
457
+ # Do nothing, leave it set to zero.
458
+ end
459
+
460
+ all_ace[:Header][:AceFlags] = INHERIT_ONLY_ACE | OBJECT_INHERIT_ACE
461
+
462
+ 2.times{
463
+ if account_rights != 0
464
+ all_ace[:Header][:AceSize] = 8 + GetLengthSid(sid)
465
+ all_ace[:Mask] = account_rights
466
+
467
+ val = AddAce(
468
+ acl_new,
469
+ ACL_REVISION2,
470
+ MAXDWORD,
471
+ all_ace,
472
+ all_ace[:Header][:AceSize]
473
+ )
474
+
475
+ raise SystemCallError.new("AddAce", FFI.errno) unless val
476
+
477
+ all_ace[:Header][:AceFlags] = CONTAINER_INHERIT_ACE
478
+ else
479
+ all_ace[:Header][:AceFlags] = 0
480
+ end
481
+
482
+ account_rights = REST_RIGHTS_MASK & mask
483
+ }
484
+ }
485
+
486
+ unless SetSecurityDescriptorDacl(sec_desc, 1, acl_new, 0)
487
+ raise SystemCallError.new("SetSecurityDescriptorDacl", FFI.errno)
488
+ end
489
+
490
+ unless SetFileSecurityW(wide_file, DACL_SECURITY_INFORMATION, sec_desc)
491
+ raise SystemCallError.new("SetFileSecurity", FFI.errno)
492
+ end
493
+
494
+ self
495
+ end
496
+
497
+ # Returns an array of human-readable strings that correspond to the
498
+ # permission flags.
499
+ #
500
+ # Example:
501
+ #
502
+ # File.get_permissions('test.txt').each{ |name, mask|
503
+ # puts name
504
+ # p File.securities(mask)
505
+ # }
506
+ #
507
+ def securities(mask)
508
+ sec_array = []
509
+
510
+ security_rights = {
511
+ 'FULL' => FULL,
512
+ 'DELETE' => DELETE,
513
+ 'READ' => READ,
514
+ 'CHANGE' => CHANGE,
515
+ 'ADD' => ADD
516
+ }
517
+
518
+ if mask == 0
519
+ sec_array.push('NONE')
520
+ else
521
+ if (mask & FULL) ^ FULL == 0
522
+ sec_array.push('FULL')
523
+ else
524
+ security_rights.each{ |string, numeric|
525
+ if (numeric & mask) ^ numeric == 0
526
+ sec_array.push(string)
527
+ end
528
+ }
529
+ end
530
+ end
531
+
532
+ sec_array
533
+ end
534
+
535
+ # Returns true if the effective user ID of the process is the same as the
536
+ # owner of the named file.
537
+ #
538
+ # Example:
539
+ #
540
+ # p File.owned?('some_file.txt') # => true
541
+ # p File.owned?('C:/Windows/regedit.ext') # => false
542
+ #--
543
+ # This method was redefined for MS Windows.
544
+ #
545
+ def owned?(file)
546
+ wide_file = string_check(file).wincode
547
+
548
+ return_value = false
549
+ size_needed_ptr = FFI::MemoryPointer.new(:ulong)
550
+
551
+ # First pass, get the size needed
552
+ bool = GetFileSecurityW(
553
+ wide_file,
554
+ OWNER_SECURITY_INFORMATION,
555
+ nil,
556
+ 0,
557
+ size_needed_ptr
558
+ )
559
+
560
+ size_needed = size_needed_ptr.read_ulong
561
+
562
+ security_ptr = FFI::MemoryPointer.new(size_needed)
563
+
564
+ # Second pass, this time with the appropriately sized security pointer
565
+ bool = GetFileSecurityW(
566
+ wide_file,
567
+ OWNER_SECURITY_INFORMATION,
568
+ security_ptr,
569
+ security_ptr.size,
570
+ size_needed_ptr
571
+ )
572
+
573
+ raise SystemCallError.new("GetFileSecurity", FFI.errno) unless bool
574
+
575
+ sid_ptr = FFI::MemoryPointer.new(:pointer)
576
+ defaulted = FFI::MemoryPointer.new(:int)
577
+
578
+ unless GetSecurityDescriptorOwner(security_ptr, sid_ptr, defaulted)
579
+ raise SystemCallError.new("GetFileSecurity", FFI.errno)
580
+ end
581
+
582
+ sid = sid_ptr.read_pointer
583
+
584
+ token = FFI::MemoryPointer.new(:uintptr_t)
585
+
586
+ begin
587
+ # Get the current process sid
588
+ unless OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, token)
589
+ raise SystemCallError, FFI.errno, "OpenProcessToken"
590
+ end
591
+
592
+ token = token.read_pointer.to_i
593
+ rlength = FFI::MemoryPointer.new(:pointer)
594
+ tuser = 0.chr * 512
595
+
596
+ bool = GetTokenInformation(
597
+ token,
598
+ TokenUser,
599
+ tuser,
600
+ tuser.size,
601
+ rlength
602
+ )
603
+
604
+ unless bool
605
+ raise SystemCallError, FFI.errno, "GetTokenInformation"
606
+ end
607
+
608
+ string_sid = tuser[FFI.type_size(:pointer)*2, (rlength.read_ulong - FFI.type_size(:pointer)*2)]
609
+
610
+ # Now compare the sid strings
611
+ if string_sid == sid.read_string(string_sid.size)
612
+ return_value = true
613
+ end
614
+ ensure
615
+ CloseHandle(token)
616
+ end
617
+
618
+ return_value
619
+ end
620
+
621
+ # Changes the owner of the named file(s) to the given owner (userid).
622
+ # It will typically require elevated privileges in order to change the
623
+ # owner of a file.
624
+ #
625
+ # This group argument is currently ignored, but is included in the method
626
+ # definition for compatibility with the current spec. Also note that the
627
+ # owner should be a string, not a numeric ID.
628
+ #
629
+ # Example:
630
+ #
631
+ # File.chown('some_user', nil, 'some_file.txt')
632
+ #--
633
+ # In the future we may allow the owner argument to be a SID or a RID and
634
+ # simply adjust accordingly.
635
+ #
636
+ def chown(owner, group, *files)
637
+ token = FFI::MemoryPointer.new(:ulong)
638
+
639
+ begin
640
+ bool = OpenProcessToken(
641
+ GetCurrentProcess(),
642
+ TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
643
+ token
644
+ )
645
+
646
+ raise SystemCallError.new("OpenProcessToken", FFI.errno) unless bool
647
+
648
+ token_handle = token.read_ulong
649
+
650
+ privs = [
651
+ SE_SECURITY_NAME,
652
+ SE_TAKE_OWNERSHIP_NAME,
653
+ SE_BACKUP_NAME,
654
+ SE_RESTORE_NAME,
655
+ SE_CHANGE_NOTIFY_NAME
656
+ ]
657
+
658
+ privs.each{ |name|
659
+ luid = LUID.new
660
+
661
+ unless LookupPrivilegeValueA(nil, name, luid)
662
+ raise SystemCallError.new("LookupPrivilegeValue", FFI.errno)
663
+ end
664
+
665
+ tp = TOKEN_PRIVILEGES.new
666
+ tp[:PrivilegeCount] = 1
667
+ tp[:Privileges][0][:Luid] = luid
668
+ tp[:Privileges][0][:Attributes] = SE_PRIVILEGE_ENABLED
669
+
670
+ unless AdjustTokenPrivileges(token_handle, 0, tp, 0, nil, nil)
671
+ raise SystemCallError.new("AdjustTokenPrivileges", FFI.errno)
672
+ end
673
+ }
674
+
675
+ sid = FFI::MemoryPointer.new(:uchar)
676
+ sid_size = FFI::MemoryPointer.new(:ulong)
677
+ dom = FFI::MemoryPointer.new(:uchar)
678
+ dom_size = FFI::MemoryPointer.new(:ulong)
679
+ use = FFI::MemoryPointer.new(:ulong)
680
+
681
+ wowner = owner.wincode
682
+
683
+ # First run, get needed sizes
684
+ LookupAccountNameW(nil, wowner, sid, sid_size, dom, dom_size, use)
685
+
686
+ sid = FFI::MemoryPointer.new(:uchar, sid_size.read_ulong * 2)
687
+ dom = FFI::MemoryPointer.new(:uchar, dom_size.read_ulong * 2)
688
+
689
+ # Second run with required sizes
690
+ unless LookupAccountNameW(nil, wowner, sid, sid_size, dom, dom_size, use)
691
+ raise SystemCallError.new("LookupAccountName", FFI.errno)
692
+ end
693
+
694
+ files.each{ |file|
695
+ wfile = string_check(file).wincode
696
+
697
+ size = FFI::MemoryPointer.new(:ulong)
698
+ sec = FFI::MemoryPointer.new(:ulong)
699
+
700
+ # First pass, get the size needed
701
+ GetFileSecurityW(wfile, OWNER_SECURITY_INFORMATION, sec, sec.size, size)
702
+
703
+ security = FFI::MemoryPointer.new(size.read_ulong)
704
+
705
+ # Second pass, this time with the appropriately sized security pointer
706
+ bool = GetFileSecurityW(
707
+ wfile,
708
+ OWNER_SECURITY_INFORMATION,
709
+ security,
710
+ security.size,
711
+ size
712
+ )
713
+
714
+ raise SystemCallError.new("GetFileSecurity", FFI.errno) unless bool
715
+
716
+ unless InitializeSecurityDescriptor(security, SECURITY_DESCRIPTOR_REVISION)
717
+ raise SystemCallError.new("InitializeSecurityDescriptor", FFI.errno)
718
+ end
719
+
720
+ unless SetSecurityDescriptorOwner(security, sid, 0)
721
+ raise SystemCallError.new("SetSecurityDescriptorOwner", FFI.errno)
722
+ end
723
+
724
+ unless SetFileSecurityW(wfile, OWNER_SECURITY_INFORMATION, security)
725
+ raise SystemCallError.new("SetFileSecurity", FFI.errno)
726
+ end
727
+ }
728
+ ensure
729
+ CloseHandle(token.read_ulong)
730
+ end
731
+
732
+ files.size
733
+ end
734
+
735
+ # Returns the owner of the specified file in domain\\userid format.
736
+ #
737
+ # Example:
738
+ #
739
+ # p File.owner('some_file.txt') # => "your_domain\\some_user"
740
+ #
741
+ def owner(file)
742
+ file = string_check(file).wincode
743
+ size_needed = FFI::MemoryPointer.new(:ulong)
744
+
745
+ # First pass, get the size needed
746
+ bool = GetFileSecurityW(
747
+ file,
748
+ OWNER_SECURITY_INFORMATION,
749
+ nil,
750
+ 0,
751
+ size_needed
752
+ )
753
+
754
+ security = FFI::MemoryPointer.new(size_needed.read_ulong)
755
+
756
+ # Second pass, this time with the appropriately sized security pointer
757
+ bool = GetFileSecurityW(
758
+ file,
759
+ OWNER_SECURITY_INFORMATION,
760
+ security,
761
+ security.size,
762
+ size_needed
763
+ )
764
+
765
+ raise SystemCallError.new("GetFileSecurity", FFI.errno) unless bool
766
+
767
+ sid = FFI::MemoryPointer.new(:pointer)
768
+ defaulted = FFI::MemoryPointer.new(:int)
769
+
770
+ unless GetSecurityDescriptorOwner(security, sid, defaulted)
771
+ raise SystemCallError.new("GetFileSecurity", FFI.errno)
772
+ end
773
+
774
+ sid = sid.read_pointer
775
+
776
+ name = FFI::MemoryPointer.new(:uchar)
777
+ name_size = FFI::MemoryPointer.new(:ulong)
778
+ dom = FFI::MemoryPointer.new(:uchar)
779
+ dom_size = FFI::MemoryPointer.new(:ulong)
780
+ use = FFI::MemoryPointer.new(:int)
781
+
782
+ # First call, get sizes needed
783
+ LookupAccountSidW(nil, sid, name, name_size, dom, dom_size, use)
784
+
785
+ name = FFI::MemoryPointer.new(:uchar, name_size.read_ulong * 2)
786
+ dom = FFI::MemoryPointer.new(:uchar, dom_size.read_ulong * 2)
787
+
788
+ # Second call, get desired information
789
+ unless LookupAccountSidW(nil, sid, name, name_size, dom, dom_size, use)
790
+ raise SystemCallError.new("LookupAccountSid", FFI.errno)
791
+ end
792
+
793
+ name = name.read_string(name.size).wstrip
794
+ domain = dom.read_string(dom.size).wstrip
795
+
796
+ domain << "\\" << name
797
+ end
798
+
799
+ # Returns the primary group of the specified file in domain\\userid format.
800
+ #
801
+ # Example:
802
+ #
803
+ # p File.group('some_file.txt') # => "your_domain\\some_group"
804
+ #
805
+ def group(file)
806
+ file = string_check(file).wincode
807
+ size_needed = FFI::MemoryPointer.new(:ulong)
808
+
809
+ # First pass, get the size needed
810
+ bool = GetFileSecurityW(
811
+ file,
812
+ GROUP_SECURITY_INFORMATION,
813
+ nil,
814
+ 0,
815
+ size_needed
816
+ )
817
+
818
+ security = FFI::MemoryPointer.new(size_needed.read_ulong)
819
+
820
+ # Second pass, this time with the appropriately sized security pointer
821
+ bool = GetFileSecurityW(
822
+ file,
823
+ GROUP_SECURITY_INFORMATION,
824
+ security,
825
+ security.size,
826
+ size_needed
827
+ )
828
+
829
+ raise SystemCallError.new("GetFileSecurity", FFI.errno) unless bool
830
+
831
+ sid = FFI::MemoryPointer.new(:pointer)
832
+ defaulted = FFI::MemoryPointer.new(:int)
833
+
834
+ unless GetSecurityDescriptorGroup(security, sid, defaulted)
835
+ raise SystemCallError.new("GetFileSecurity", FFI.errno)
836
+ end
837
+
838
+ sid = sid.read_pointer
839
+
840
+ name = FFI::MemoryPointer.new(:uchar)
841
+ name_size = FFI::MemoryPointer.new(:ulong)
842
+ dom = FFI::MemoryPointer.new(:uchar)
843
+ dom_size = FFI::MemoryPointer.new(:ulong)
844
+ use = FFI::MemoryPointer.new(:int)
845
+
846
+ # First call, get sizes needed
847
+ LookupAccountSidW(nil, sid, name, name_size, dom, dom_size, use)
848
+
849
+ name = FFI::MemoryPointer.new(:uchar, name_size.read_ulong * 2)
850
+ dom = FFI::MemoryPointer.new(:uchar, dom_size.read_ulong * 2)
851
+
852
+ # Second call, get desired information
853
+ unless LookupAccountSidW(nil, sid, name, name_size, dom, dom_size, use)
854
+ raise SystemCallError.new("LookupAccountSid", FFI.errno)
855
+ end
856
+
857
+ name = name.read_string(name.size).wstrip
858
+ domain = dom.read_string(dom.size).wstrip
859
+
860
+ domain << "\\" << name
861
+ end
862
+
863
+ # Returns true if the primary group ID of the process is the same
864
+ # as the owner of the named file.
865
+ #
866
+ # Example:
867
+ #
868
+ # p File.grpowned?('some_file.txt') # => true
869
+ # p File.grpowned?('C:/Windows/regedit.exe') # => false
870
+ #--
871
+ # This method was redefined for MS Windows.
872
+ #
873
+ def grpowned?(file)
874
+ wide_file = string_check(file).wincode
875
+
876
+ return_value = false
877
+ size_needed_ptr = FFI::MemoryPointer.new(:ulong)
878
+
879
+ # First pass, get the size needed
880
+ bool = GetFileSecurityW(
881
+ wide_file,
882
+ GROUP_SECURITY_INFORMATION,
883
+ nil,
884
+ 0,
885
+ size_needed_ptr
886
+ )
887
+
888
+ size_needed = size_needed_ptr.read_ulong
889
+
890
+ security_ptr = FFI::MemoryPointer.new(size_needed)
891
+
892
+ # Second pass, this time with the appropriately sized security pointer
893
+ bool = GetFileSecurityW(
894
+ wide_file,
895
+ GROUP_SECURITY_INFORMATION,
896
+ security_ptr,
897
+ security_ptr.size,
898
+ size_needed_ptr
899
+ )
900
+
901
+ raise SystemCallError.new("GetFileSecurity", FFI.errno) unless bool
902
+
903
+ sid_ptr = FFI::MemoryPointer.new(:pointer)
904
+ defaulted = FFI::MemoryPointer.new(:int)
905
+ pstring_sid = FFI::MemoryPointer.new(:string)
906
+
907
+ unless GetSecurityDescriptorGroup(security_ptr, sid_ptr, defaulted)
908
+ raise SystemCallError.new("GetFileSecurity", FFI.errno)
909
+ end
910
+
911
+ sid = sid_ptr.read_pointer
912
+ ConvertSidToStringSidA(sid, pstring_sid)
913
+ file_string_sid = pstring_sid.read_pointer.read_string
914
+
915
+ token = FFI::MemoryPointer.new(:uintptr_t)
916
+
917
+ begin
918
+ # Get the current process sid
919
+ unless OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, token)
920
+ raise SystemCallError, FFI.errno, "OpenProcessToken"
921
+ end
922
+
923
+ token = token.read_pointer.to_i
924
+ rlength = FFI::MemoryPointer.new(:ulong)
925
+ tgroup = TOKEN_GROUP.new
926
+
927
+ bool = GetTokenInformation(
928
+ token,
929
+ TokenGroups,
930
+ tgroup,
931
+ tgroup.size,
932
+ rlength
933
+ )
934
+
935
+ unless bool
936
+ raise SystemCallError.new("GetTokenInformation", FFI.errno)
937
+ end
938
+
939
+ ConvertSidToStringSidA(tgroup[:Groups][0][:Sid], pstring_sid)
940
+ string_sid = pstring_sid.read_pointer.read_string
941
+
942
+ # Now compare the sid strings
943
+ if string_sid == file_string_sid
944
+ return_value = true
945
+ end
946
+ ensure
947
+ CloseHandle(token)
948
+ end
949
+
950
+ return_value
951
+ end
952
+
953
+ private
954
+
955
+ # Simulate MRI's contortions for a stringiness check.
956
+ def string_check(arg)
957
+ return arg if arg.is_a?(String)
958
+ return arg.send(:to_str) if arg.respond_to?(:to_str, true) # MRI honors it, even if private
959
+ return arg.to_path if arg.respond_to?(:to_path)
960
+ raise TypeError
961
+ end
962
+ end
963
+ end