win32-file 0.5.3 → 0.5.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/CHANGES CHANGED
@@ -1,3 +1,9 @@
1
+ == 0.5.4 - 8-Apr-2007
2
+ * Now runs -w clean.
3
+ * Added a Rakefile. Manual installation and testing should now be handled
4
+ by the Rake tasks.
5
+ * Some updates to the README file.
6
+
1
7
  == 0.5.3 - 2-Nov-2006
2
8
  * Added the File.lstat method. It's abscence caused problems for cross
3
9
  platform packages (such as the 'find' module) which were expecting a result
data/MANIFEST CHANGED
@@ -1,16 +1,15 @@
1
- CHANGES
2
- MANIFEST
3
- README
4
- install.rb
5
- win32-file.gemspec
6
-
7
- lib/win32/file.rb
8
-
9
- test/sometestfile.txt
10
- test/tc_file_attributes.rb
11
- test/tc_file_constants.rb
12
- test/tc_file_encryption.rb
13
- test/tc_file_path.rb
14
- test/tc_file_security.rb
15
- test/tc_file_stat.rb
16
- test/ts_all.rb
1
+ * CHANGES
2
+ * MANIFEST
3
+ * Rakefile
4
+ * README
5
+ * install.rb
6
+ * win32-file.gemspec
7
+ * lib/win32/file.rb
8
+ * test/sometestfile.txt
9
+ * test/tc_file_attributes.rb
10
+ * test/tc_file_constants.rb
11
+ * test/tc_file_encryption.rb
12
+ * test/tc_file_path.rb
13
+ * test/tc_file_security.rb
14
+ * test/tc_file_stat.rb
15
+ * test/ts_all.rb
data/README CHANGED
@@ -2,27 +2,23 @@
2
2
  Extra or redefined methods for the File class on MS Windows.
3
3
 
4
4
  = Prerequisites
5
- Ruby 1.8.0 or later
6
- windows-pr 0.3.0 or later
7
- win32-file-stat 1.2.0 or later
5
+ * Ruby 1.8.0 or later
6
+ * windows-pr 0.3.0 or later
7
+ * win32-file-stat 1.2.0 or later
8
8
 
9
9
  = Installation
10
- == Manual install
11
- ruby test/ts_all.rb # optional
12
- ruby install.rb
13
- == Gem Install
14
- ruby win32-file.gemspec
15
- gem install win32-file-X.Y.Z-mswin32.gem
10
+ rake test (optional)
11
+ rake install (standard) or rake install_gem (rubygems)
16
12
 
17
13
  = Synopsis
18
- require 'win32/file'
14
+ require 'win32/file'
19
15
 
20
- p File.hidden?(somefile)
21
- p File.attributes(somefile)
16
+ p File.hidden?(somefile)
17
+ p File.attributes(somefile)
22
18
 
23
- File.open(somefile){ |fh|
24
- fh.hidden = true
25
- }
19
+ File.open(somefile){ |fh|
20
+ fh.hidden = true
21
+ }
26
22
 
27
23
  = Class methods added - see documentation for details
28
24
  * File.attributes
@@ -94,5 +90,5 @@ implied warranties, including, without limitation, the implied
94
90
  warranties of merchantability and fitness for a particular purpose.
95
91
 
96
92
  = Authors
97
- Daniel J. Berger
98
- Park Heesob
93
+ * Daniel J. Berger
94
+ * Park Heesob
@@ -0,0 +1,66 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+
4
+ task :clean do
5
+ rm 'sometestfile.txt' if File.exists? 'sometestfile.txt'
6
+ end
7
+
8
+ desc 'Install the win32-file package (non-gem)'
9
+ task :install => [:clean] do
10
+ ruby 'install.rb'
11
+ end
12
+
13
+ desc 'Install the win32-file package as a gem'
14
+ task :install_gem => [:clean] do
15
+ ruby 'win32-file.gemspec'
16
+ file = Dir['win32-file*.gem'].first
17
+ sh "gem install #{file}"
18
+ end
19
+
20
+ Rake::TestTask.new("test") do |t|
21
+ t.libs << 'test'
22
+ t.verbose = true
23
+ t.warning = true
24
+ t.test_files = FileList['test/ts_all.rb']
25
+ end
26
+
27
+ Rake::TestTask.new("test_attributes") do |t|
28
+ cp('test/sometestfile.txt', '.')
29
+ t.verbose = true
30
+ t.warning = true
31
+ t.test_files = FileList['test/tc_file_attributes.rb']
32
+ end
33
+
34
+ Rake::TestTask.new("test_constants") do |t|
35
+ t.verbose = true
36
+ t.warning = true
37
+ t.test_files = FileList['test/tc_file_constants.rb']
38
+ end
39
+
40
+ Rake::TestTask.new("test_encryption") do |t|
41
+ cp('test/sometestfile.txt', '.')
42
+ t.verbose = true
43
+ t.warning = true
44
+ t.test_files = FileList['test/tc_file_encryption.rb']
45
+ end
46
+
47
+ Rake::TestTask.new("test_path") do |t|
48
+ cp('test/sometestfile.txt', '.')
49
+ t.verbose = true
50
+ t.warning = true
51
+ t.test_files = FileList['test/tc_file_path.rb']
52
+ end
53
+
54
+ Rake::TestTask.new("test_security") do |t|
55
+ cp('test/sometestfile.txt', '.')
56
+ t.verbose = true
57
+ t.warning = true
58
+ t.test_files = FileList['test/tc_file_security.rb']
59
+ end
60
+
61
+ Rake::TestTask.new("test_stat") do |t|
62
+ cp('test/sometestfile.txt', '.')
63
+ t.verbose = true
64
+ t.warning = true
65
+ t.test_files = FileList['test/tc_file_stat.rb']
66
+ end
@@ -14,8 +14,9 @@ class File
14
14
  extend Windows::Path
15
15
  extend Windows::Security
16
16
  extend Windows::MSVCRT::Buffer
17
+ extend Windows::Limits
17
18
 
18
- VERSION = '0.5.3'
19
+ VERSION = '0.5.4'
19
20
  MAX_PATH = 260
20
21
 
21
22
  # Abbreviated attribute constants for convenience
@@ -48,510 +49,529 @@ class File
48
49
  }
49
50
 
50
51
  ### Class Methods
51
-
52
- ## Security
53
-
54
- # Sets the file permissions for the given file name. The 'permissions'
55
- # argument is a hash with an account name as the key, and the various
56
- # permission constants as possible values. The possible constant values
57
- # are:
58
- #
59
- # FILE_READ_DATA
60
- # FILE_WRITE_DATA
61
- # FILE_APPEND_DATA
62
- # FILE_READ_EA
63
- # FILE_WRITE_EA
64
- # FILE_EXECUTE
65
- # FILE_DELETE_CHILD
66
- # FILE_READ_ATTRIBUTES
67
- # FILE_WRITE_ATTRIBUTES
68
- # STANDARD_RIGHTS_ALL
69
- # FULL
70
- # READ
71
- # ADD
72
- # CHANGE
73
- # DELETE
74
- # READ_CONTROL
75
- # WRITE_DAC
76
- # WRITE_OWNER
77
- # SYNCHRONIZE
78
- # STANDARD_RIGHTS_REQUIRED
79
- # STANDARD_RIGHTS_READ
80
- # STANDARD_RIGHTS_WRITE
81
- # STANDARD_RIGHTS_EXECUTE
82
- # STANDARD_RIGHTS_ALL
83
- # SPECIFIC_RIGHTS_ALL
84
- # ACCESS_SYSTEM_SECURITY
85
- # MAXIMUM_ALLOWED
86
- # GENERIC_READ
87
- # GENERIC_WRITE
88
- # GENERIC_EXECUTE
89
- # GENERIC_ALL
90
- #
91
- def self.set_permissions(file, perms)
92
- raise TypeError unless perms.kind_of?(Hash)
93
-
94
- account_rights = 0
95
- sec_desc = 0.chr * SECURITY_DESCRIPTOR_MIN_LENGTH
96
-
97
- unless InitializeSecurityDescriptor(sec_desc, 1)
98
- raise ArgumentError, get_last_error
99
- end
100
-
101
- cb_acl = 1024
102
- cb_sid = 1024
103
-
104
- acl_new = 0.chr * cb_acl
105
-
106
- unless InitializeAcl(acl_new, cb_acl, ACL_REVISION2)
107
- raise ArgumentError, get_last_error
108
- end
109
-
110
- sid = 0.chr * cb_sid
111
- snu_type = 0.chr * cb_sid
112
-
113
- all_ace = 0.chr * ALLOW_ACE_LENGTH
114
- all_ace_ptr = memset(all_ace, 0, 0) # address of all_ace
115
52
 
116
- # all_ace_ptr->Header.AceType = ACCESS_ALLOWED_ACE_TYPE
117
- all_ace[0] = 0
118
-
119
- perms.each{ |account, mask|
120
- next if mask.nil?
121
-
122
- cch_domain = [80].pack('L')
123
- cb_sid = [1024].pack('L')
124
- domain_buf = 0.chr * 80
125
-
126
- server, account = account.split("\\")
127
-
128
- if ['BUILTIN', 'NT AUTHORITY'].include?(server.upcase)
129
- server = nil
53
+ class << self
54
+ # Strictly for making this code -w clean. They are removed later.
55
+ alias basename_orig basename
56
+ alias blockdev_orig blockdev?
57
+ alias chardev_orig chardev?
58
+ alias dirname_orig dirname
59
+ alias lstat_orig lstat
60
+ alias size_orig size
61
+ alias split_orig split
62
+ alias stat_orig stat
63
+
64
+ ## Security
65
+
66
+ # Sets the file permissions for the given file name. The 'permissions'
67
+ # argument is a hash with an account name as the key, and the various
68
+ # permission constants as possible values. The possible constant values
69
+ # are:
70
+ #
71
+ # FILE_READ_DATA
72
+ # FILE_WRITE_DATA
73
+ # FILE_APPEND_DATA
74
+ # FILE_READ_EA
75
+ # FILE_WRITE_EA
76
+ # FILE_EXECUTE
77
+ # FILE_DELETE_CHILD
78
+ # FILE_READ_ATTRIBUTES
79
+ # FILE_WRITE_ATTRIBUTES
80
+ # STANDARD_RIGHTS_ALL
81
+ # FULL
82
+ # READ
83
+ # ADD
84
+ # CHANGE
85
+ # DELETE
86
+ # READ_CONTROL
87
+ # WRITE_DAC
88
+ # WRITE_OWNER
89
+ # SYNCHRONIZE
90
+ # STANDARD_RIGHTS_REQUIRED
91
+ # STANDARD_RIGHTS_READ
92
+ # STANDARD_RIGHTS_WRITE
93
+ # STANDARD_RIGHTS_EXECUTE
94
+ # STANDARD_RIGHTS_ALL
95
+ # SPECIFIC_RIGHTS_ALL
96
+ # ACCESS_SYSTEM_SECURITY
97
+ # MAXIMUM_ALLOWED
98
+ # GENERIC_READ
99
+ # GENERIC_WRITE
100
+ # GENERIC_EXECUTE
101
+ # GENERIC_ALL
102
+ #
103
+ def set_permissions(file, perms)
104
+ raise TypeError unless perms.kind_of?(Hash)
105
+
106
+ account_rights = 0
107
+ sec_desc = 0.chr * SECURITY_DESCRIPTOR_MIN_LENGTH
108
+
109
+ unless InitializeSecurityDescriptor(sec_desc, 1)
110
+ raise ArgumentError, get_last_error
130
111
  end
131
112
 
132
- val = LookupAccountName(
133
- server,
134
- account,
135
- sid,
136
- cb_sid,
137
- domain_buf,
138
- cch_domain,
139
- snu_type
140
- )
113
+ cb_acl = 1024
114
+ cb_sid = 1024
141
115
 
142
- if val == 0
116
+ acl_new = 0.chr * cb_acl
117
+
118
+ unless InitializeAcl(acl_new, cb_acl, ACL_REVISION2)
143
119
  raise ArgumentError, get_last_error
144
120
  end
145
121
 
146
- size = [0,0,0,0,0].pack('CCSLL').length # sizeof(ACCESS_ALLOWED_ACE)
147
-
148
- val = CopySid(
149
- ALLOW_ACE_LENGTH - size,
150
- all_ace_ptr + 8, # address of all_ace_ptr->SidStart
151
- sid
152
- )
122
+ sid = 0.chr * cb_sid
123
+ snu_type = 0.chr * cb_sid
153
124
 
154
- if val == 0
155
- raise ArgumentError, get_last_error
156
- end
125
+ all_ace = 0.chr * ALLOW_ACE_LENGTH
126
+ all_ace_ptr = memset(all_ace, 0, 0) # address of all_ace
157
127
 
158
- if (GENERIC_ALL & mask).nonzero?
159
- account_rights = GENERIC_ALL & mask
160
- elsif (GENERIC_RIGHTS_CHK & mask).nonzero?
161
- account_rights = GENERIC_RIGHTS_MASK & mask
162
- end
128
+ # all_ace_ptr->Header.AceType = ACCESS_ALLOWED_ACE_TYPE
129
+ all_ace[0] = 0
163
130
 
164
- # all_ace_ptr->Header.AceFlags = INHERIT_ONLY_ACE | OBJECT_INHERIT_ACE;
165
- all_ace[1] = INHERIT_ONLY_ACE | OBJECT_INHERIT_ACE
131
+ perms.each{ |account, mask|
132
+ next if mask.nil?
133
+
134
+ cch_domain = [80].pack('L')
135
+ cb_sid = [1024].pack('L')
136
+ domain_buf = 0.chr * 80
166
137
 
167
- 2.times{
168
- if account_rights != 0
169
- all_ace[2,2] = [12 - 4 + GetLengthSid(sid)].pack('S')
170
- all_ace[4,4] = [account_rights].pack('L')
138
+ server, account = account.split("\\")
171
139
 
172
- val = AddAce(
173
- acl_new,
174
- ACL_REVISION2,
175
- MAXDWORD,
176
- all_ace_ptr,
177
- all_ace[2,2].unpack('S').first
178
- )
140
+ if ['BUILTIN', 'NT AUTHORITY'].include?(server.upcase)
141
+ server = nil
142
+ end
179
143
 
180
- if val == 0
181
- raise ArgumentError, get_last_error
182
- end
144
+ val = LookupAccountName(
145
+ server,
146
+ account,
147
+ sid,
148
+ cb_sid,
149
+ domain_buf,
150
+ cch_domain,
151
+ snu_type
152
+ )
183
153
 
184
- # all_ace_ptr->Header.AceFlags = CONTAINER_INHERIT_ACE
185
- all_ace[1] = CONTAINER_INHERIT_ACE
186
- else
187
- # all_ace_ptr->Header.AceFlags = 0
188
- all_ace[1] = 0
154
+ if val == 0
155
+ raise ArgumentError, get_last_error
189
156
  end
190
157
 
191
- account_rights = REST_RIGHTS_MASK & mask
192
- }
193
- }
158
+ size = [0,0,0,0,0].pack('CCSLL').length # sizeof(ACCESS_ALLOWED_ACE)
159
+
160
+ val = CopySid(
161
+ ALLOW_ACE_LENGTH - size,
162
+ all_ace_ptr + 8, # address of all_ace_ptr->SidStart
163
+ sid
164
+ )
194
165
 
195
- unless SetSecurityDescriptorDacl(sec_desc, 1, acl_new, 0)
196
- raise ArgumentError, get_last_error
197
- end
166
+ if val == 0
167
+ raise ArgumentError, get_last_error
168
+ end
198
169
 
199
- unless SetFileSecurity(file, DACL_SECURITY_INFORMATION, sec_desc)
200
- raise ArgumentError, get_last_error
201
- end
170
+ if (GENERIC_ALL & mask).nonzero?
171
+ account_rights = GENERIC_ALL & mask
172
+ elsif (GENERIC_RIGHTS_CHK & mask).nonzero?
173
+ account_rights = GENERIC_RIGHTS_MASK & mask
174
+ end
202
175
 
203
- self
204
- end
205
-
206
- # Returns an array of human-readable strings that correspond to the
207
- # permission flags.
208
- #
209
- def self.securities(mask)
210
- sec_array = []
211
- if mask == 0
212
- sec_array.push('NONE')
213
- else
214
- if (mask & FULL) ^ FULL == 0
215
- sec_array.push('FULL')
216
- else
217
- SECURITY_RIGHTS.each{ |string, numeric|
218
- if (numeric & mask) ^ numeric == 0
219
- sec_array.push(string)
176
+ # all_ace_ptr->Header.AceFlags = INHERIT_ONLY_ACE|OBJECT_INHERIT_ACE
177
+ all_ace[1] = INHERIT_ONLY_ACE | OBJECT_INHERIT_ACE
178
+
179
+ 2.times{
180
+ if account_rights != 0
181
+ all_ace[2,2] = [12 - 4 + GetLengthSid(sid)].pack('S')
182
+ all_ace[4,4] = [account_rights].pack('L')
183
+
184
+ val = AddAce(
185
+ acl_new,
186
+ ACL_REVISION2,
187
+ MAXDWORD,
188
+ all_ace_ptr,
189
+ all_ace[2,2].unpack('S').first
190
+ )
191
+
192
+ if val == 0
193
+ raise ArgumentError, get_last_error
194
+ end
195
+
196
+ # all_ace_ptr->Header.AceFlags = CONTAINER_INHERIT_ACE
197
+ all_ace[1] = CONTAINER_INHERIT_ACE
198
+ else
199
+ # all_ace_ptr->Header.AceFlags = 0
200
+ all_ace[1] = 0
220
201
  end
202
+
203
+ account_rights = REST_RIGHTS_MASK & mask
221
204
  }
222
- end
223
- end
224
- sec_array
225
- end
205
+ }
226
206
 
227
- # Returns a hash describing the current file permissions for the given file.
228
- # The account name is the key, and the value is an integer representing
229
- # an or'd value that corresponds to the security permissions for that file.
230
- #
231
- # To get a human readable version of the permissions, pass the value to the
232
- # +File.securities+ method.
233
- #
234
- def self.get_permissions(file, host=nil)
235
- current_length = 0
236
- length_needed = [0].pack('L')
237
- sec_buf = ''
238
-
239
- loop do
240
- bool = GetFileSecurity(
241
- file,
242
- DACL_SECURITY_INFORMATION,
243
- sec_buf,
244
- sec_buf.length,
245
- length_needed
246
- )
207
+ unless SetSecurityDescriptorDacl(sec_desc, 1, acl_new, 0)
208
+ raise ArgumentError, get_last_error
209
+ end
247
210
 
248
- if bool == 0 && GetLastError() != ERROR_INSUFFICIENT_BUFFER
211
+ unless SetFileSecurity(file, DACL_SECURITY_INFORMATION, sec_desc)
249
212
  raise ArgumentError, get_last_error
250
213
  end
251
-
252
- break if sec_buf.length >= length_needed.unpack('L').first
253
- sec_buf += ' ' * length_needed.unpack('L').first
214
+
215
+ self
216
+ end
217
+
218
+ # Returns an array of human-readable strings that correspond to the
219
+ # permission flags.
220
+ #
221
+ def securities(mask)
222
+ sec_array = []
223
+ if mask == 0
224
+ sec_array.push('NONE')
225
+ else
226
+ if (mask & FULL) ^ FULL == 0
227
+ sec_array.push('FULL')
228
+ else
229
+ SECURITY_RIGHTS.each{ |string, numeric|
230
+ if (numeric & mask) ^ numeric == 0
231
+ sec_array.push(string)
232
+ end
233
+ }
234
+ end
235
+ end
236
+ sec_array
254
237
  end
255
238
 
256
- control = [0].pack('L')
257
- revision = [0].pack('L')
239
+ # Returns a hash describing the current file permissions for the given
240
+ # file. The account name is the key, and the value is an integer
241
+ # representing an or'd value that corresponds to the security
242
+ # permissions for that file.
243
+ #
244
+ # To get a human readable version of the permissions, pass the value to
245
+ # the +File.securities+ method.
246
+ #
247
+ def get_permissions(file, host=nil)
248
+ current_length = 0
249
+ length_needed = [0].pack('L')
250
+ sec_buf = ''
251
+
252
+ loop do
253
+ bool = GetFileSecurity(
254
+ file,
255
+ DACL_SECURITY_INFORMATION,
256
+ sec_buf,
257
+ sec_buf.length,
258
+ length_needed
259
+ )
258
260
 
259
- unless GetSecurityDescriptorControl(sec_buf, control, revision)
260
- raise ArgumentError, get_last_error
261
- end
261
+ if bool == 0 && GetLastError() != ERROR_INSUFFICIENT_BUFFER
262
+ raise ArgumentError, get_last_error
263
+ end
264
+
265
+ break if sec_buf.length >= length_needed.unpack('L').first
266
+ sec_buf += ' ' * length_needed.unpack('L').first
267
+ end
262
268
 
263
- # No DACL exists
264
- if (control.unpack('L').first & SE_DACL_PRESENT) == 0
265
- raise ArgumentError, 'No DACL present: explicit deny all'
266
- end
269
+ control = [0].pack('L')
270
+ revision = [0].pack('L')
267
271
 
268
- dacl_present = [0].pack('L')
269
- dacl_defaulted = [0].pack('L')
270
- dacl_ptr = [0].pack('L')
272
+ unless GetSecurityDescriptorControl(sec_buf, control, revision)
273
+ raise ArgumentError, get_last_error
274
+ end
271
275
 
272
- val = GetSecurityDescriptorDacl(
273
- sec_buf,
274
- dacl_present,
275
- dacl_ptr,
276
- dacl_defaulted
277
- )
276
+ # No DACL exists
277
+ if (control.unpack('L').first & SE_DACL_PRESENT) == 0
278
+ raise ArgumentError, 'No DACL present: explicit deny all'
279
+ end
278
280
 
279
- if val == 0
280
- raise ArgumentError, get_last_error
281
- end
281
+ dacl_present = [0].pack('L')
282
+ dacl_defaulted = [0].pack('L')
283
+ dacl_ptr = [0].pack('L')
282
284
 
283
- acl_buf = 0.chr * 8 # byte, byte, word, word, word (struct ACL)
284
- memcpy(acl_buf, dacl_ptr.unpack('L').first, acl_buf.size)
285
+ val = GetSecurityDescriptorDacl(
286
+ sec_buf,
287
+ dacl_present,
288
+ dacl_ptr,
289
+ dacl_defaulted
290
+ )
285
291
 
286
- if acl_buf.unpack('CCSSS').first == 0
287
- raise ArgumentError, 'DACL is NULL: implicit access grant'
288
- end
292
+ if val == 0
293
+ raise ArgumentError, get_last_error
294
+ end
289
295
 
290
- ace_ptr = [0].pack('L')
291
- ace_count = acl_buf.unpack('CCSSS')[3]
296
+ acl_buf = 0.chr * 8 # byte, byte, word, word, word (struct ACL)
297
+ memcpy(acl_buf, dacl_ptr.unpack('L').first, acl_buf.size)
292
298
 
293
- perms_hash = {}
294
- 0.upto(ace_count - 1){ |i|
295
- unless GetAce(dacl_ptr.unpack('L').first, i, ace_ptr)
296
- next
299
+ if acl_buf.unpack('CCSSS').first == 0
300
+ raise ArgumentError, 'DACL is NULL: implicit access grant'
297
301
  end
298
302
 
299
- ace_buf = 0.chr * 12 # ACE_HEADER, dword, dword (ACCESS_ALLOWED_ACE)
300
- memcpy(ace_buf, ace_ptr.unpack('L').first, ace_buf.size)
303
+ ace_ptr = [0].pack('L')
304
+ ace_count = acl_buf.unpack('CCSSS')[3]
301
305
 
302
- if ace_buf.unpack('CCS').first == ACCESS_ALLOWED_ACE_TYPE
303
- name = 0.chr * MAX_PATH
304
- name_size = [name.size].pack('L')
305
- domain = 0.chr * MAX_PATH
306
- domain_size = [domain.size].pack('L')
307
- snu_ptr = 0.chr * 4
308
-
309
- val = LookupAccountSid(
310
- host,
311
- ace_ptr.unpack('L').first + 8, # address of ace_ptr->SidStart
312
- name,
313
- name_size,
314
- domain,
315
- domain_size,
316
- snu_ptr
317
- )
318
-
319
- if val == 0
320
- raise ArgumentError, get_last_error
306
+ perms_hash = {}
307
+ 0.upto(ace_count - 1){ |i|
308
+ unless GetAce(dacl_ptr.unpack('L').first, i, ace_ptr)
309
+ next
321
310
  end
322
-
323
- name = name[0..name_size.unpack('L').first].split(0.chr)[0]
324
- domain = domain[0..domain_size.unpack('L').first].split(0.chr)[0]
325
- mask = ace_buf.unpack('LLL')[1]
326
-
327
- unless domain.nil? || domain.empty?
328
- name = domain + '\\' + name
311
+
312
+ ace_buf = 0.chr * 12 # ACE_HEADER, dword, dword (ACCESS_ALLOWED_ACE)
313
+ memcpy(ace_buf, ace_ptr.unpack('L').first, ace_buf.size)
314
+
315
+ if ace_buf.unpack('CCS').first == ACCESS_ALLOWED_ACE_TYPE
316
+ name = 0.chr * MAX_PATH
317
+ name_size = [name.size].pack('L')
318
+ domain = 0.chr * MAX_PATH
319
+ domain_size = [domain.size].pack('L')
320
+ snu_ptr = 0.chr * 4
321
+
322
+ val = LookupAccountSid(
323
+ host,
324
+ ace_ptr.unpack('L').first + 8, # address of ace_ptr->SidStart
325
+ name,
326
+ name_size,
327
+ domain,
328
+ domain_size,
329
+ snu_ptr
330
+ )
331
+
332
+ if val == 0
333
+ raise ArgumentError, get_last_error
334
+ end
335
+
336
+ name = name[0..name_size.unpack('L').first].split(0.chr)[0]
337
+ domain = domain[0..domain_size.unpack('L').first].split(0.chr)[0]
338
+ mask = ace_buf.unpack('LLL')[1]
339
+
340
+ unless domain.nil? || domain.empty?
341
+ name = domain + '\\' + name
342
+ end
343
+
344
+ perms_hash[name] = mask
329
345
  end
330
-
331
- perms_hash[name] = mask
346
+ }
347
+ perms_hash
348
+ end
349
+
350
+ ## Encryption
351
+
352
+ # Encrypts a file or directory. All data streams in a file are encrypted.
353
+ # All new files created in an encrypted directory are encrypted.
354
+ #
355
+ # The caller must have the FILE_READ_DATA, FILE_WRITE_DATA,
356
+ # FILE_READ_ATTRIBUTES, FILE_WRITE_ATTRIBUTES, and SYNCHRONIZE access
357
+ # rights.
358
+ #
359
+ # Requires exclusive access to the file being encrypted, and will fail if
360
+ # another process is using the file. If the file is compressed,
361
+ # EncryptFile will decompress the file before encrypting it.
362
+ #
363
+ # Windows 2000 or later only.
364
+ #
365
+ def encrypt(file)
366
+ unless EncryptFile(file)
367
+ raise ArgumentError, get_last_error
332
368
  end
333
- }
334
- perms_hash
335
- end
336
-
337
- ## Encryption
338
-
339
- # Encrypts a file or directory. All data streams in a file are encrypted.
340
- # All new files created in an encrypted directory are encrypted.
341
- #
342
- # The caller must have the FILE_READ_DATA, FILE_WRITE_DATA,
343
- # FILE_READ_ATTRIBUTES, FILE_WRITE_ATTRIBUTES, and SYNCHRONIZE access
344
- # rights.
345
- #
346
- # Requires exclusive access to the file being encrypted, and will fail if
347
- # another process is using the file. If the file is compressed, EncryptFile
348
- # will decompress the file before encrypting it.
349
- #
350
- # Windows 2000 or later only.
351
- #
352
- def self.encrypt(file)
353
- unless EncryptFile(file)
354
- raise ArgumentError, get_last_error
369
+ self
355
370
  end
356
- self
357
- end
358
-
359
- # Decrypts an encrypted file or directory.
360
- #
361
- # The caller must have the FILE_READ_DATA, FILE_WRITE_DATA,
362
- # FILE_READ_ATTRIBUTES, FILE_WRITE_ATTRIBUTES, and SYNCHRONIZE access
363
- # rights.
364
- #
365
- # Requires exclusive access to the file being decrypted, and will fail if
366
- # another process is using the file. If the file is not encrypted an error
367
- # is NOT raised.
368
- #
369
- # Windows 2000 or later only.
370
- #
371
- def self.decrypt(file)
372
- unless DecryptFile(file, 0)
373
- raise ArgumentError, get_last_error
371
+
372
+ # Decrypts an encrypted file or directory.
373
+ #
374
+ # The caller must have the FILE_READ_DATA, FILE_WRITE_DATA,
375
+ # FILE_READ_ATTRIBUTES, FILE_WRITE_ATTRIBUTES, and SYNCHRONIZE access
376
+ # rights.
377
+ #
378
+ # Requires exclusive access to the file being decrypted, and will fail if
379
+ # another process is using the file. If the file is not encrypted an error
380
+ # is NOT raised.
381
+ #
382
+ # Windows 2000 or later only.
383
+ #
384
+ def decrypt(file)
385
+ unless DecryptFile(file, 0)
386
+ raise ArgumentError, get_last_error
387
+ end
388
+ self
374
389
  end
375
- self
376
- end
377
390
 
378
- ## Path methods
379
-
380
- # Returns the last component of the filename given in +filename+. If
381
- # +suffix+ is given and present at the end of +filename+, it is removed.
382
- # Any extension can be removed by giving an extension of ".*".
383
- #
384
- # This was reimplemented because the current version does not handle UNC
385
- # paths properly, i.e. it should not return anything less than the root.
386
- # In all other respects it is identical to the current implementation.
387
- #
388
- # File.basename("C:\\foo\\bar.txt") -> "bar.txt"
389
- # File.basename("C:\\foo\\bar.txt", ".txt") -> "bar"
390
- # File.basename("\\\\foo\\bar") -> "\\\\foo\\bar"
391
- #
392
- def self.basename(file, suffix = nil)
393
- fpath = false
394
- file = file.dup # Don't modify original string
391
+ ## Path methods
395
392
 
396
- # We have to convert forward slashes to backslashes for the Windows
397
- # functions to work properly.
398
- if file.include?('/')
399
- file.tr!('/', '\\')
400
- fpath = true
393
+ # Returns the last component of the filename given in +filename+. If
394
+ # +suffix+ is given and present at the end of +filename+, it is removed.
395
+ # Any extension can be removed by giving an extension of ".*".
396
+ #
397
+ # This was reimplemented because the current version does not handle UNC
398
+ # paths properly, i.e. it should not return anything less than the root.
399
+ # In all other respects it is identical to the current implementation.
400
+ #
401
+ # File.basename("C:\\foo\\bar.txt") -> "bar.txt"
402
+ # File.basename("C:\\foo\\bar.txt", ".txt") -> "bar"
403
+ # File.basename("\\\\foo\\bar") -> "\\\\foo\\bar"
404
+ #
405
+ def basename(file, suffix = nil)
406
+ fpath = false
407
+ file = file.dup # Don't modify original string
408
+
409
+ # We have to convert forward slashes to backslashes for the Windows
410
+ # functions to work properly.
411
+ if file.include?('/')
412
+ file.tr!('/', '\\')
413
+ fpath = true
414
+ end
415
+
416
+ # Return an empty or root path as-is.
417
+ if file.empty? || PathIsRoot(file)
418
+ file.tr!("\\", '/') if fpath
419
+ return file
420
+ end
421
+
422
+ PathStripPath(file) # Gives us the basename
423
+
424
+ if suffix
425
+ if suffix == '.*'
426
+ PathRemoveExtension(file)
427
+ else
428
+ if PathFindExtension(file) == suffix
429
+ PathRemoveExtension(file)
430
+ end
431
+ end
432
+ end
433
+
434
+ file = file.split(0.chr).first
435
+
436
+ # Trim trailing slashes
437
+ while file[-1].chr == "\\"
438
+ file.chop!
439
+ end
440
+
441
+ # Return forward slashes if that's how the path was passed in.
442
+ if fpath
443
+ file.tr!("\\", '/')
444
+ end
445
+
446
+ file
401
447
  end
402
-
403
- # Return an empty or root path as-is.
404
- if file.empty? || PathIsRoot(file)
448
+
449
+ # Returns all components of the filename given in +filename+ except the
450
+ # last one.
451
+ #
452
+ # This was reimplemented because the current version does not handle UNC
453
+ # paths properly, i.e. it should not return anything less than the root.
454
+ # In all other respects it is identical to the current implementation.
455
+ #
456
+ # File.dirname("C:\\foo\\bar\\baz.txt") -> "C:\\foo\\bar"
457
+ # File.dirname("\\\\foo\\bar") -> "\\\\foo\\bar"
458
+ #
459
+ def dirname(file)
460
+ fpath = false
461
+ file = file.dup
462
+
463
+ if file.include?('/')
464
+ file.tr!('/', "\\")
465
+ fpath = true
466
+ end
467
+
468
+ if PathIsRelative(file)
469
+ return '.'
470
+ end
471
+
472
+ if PathIsRoot(file)
473
+ file.tr!("\\", '/') if fpath
474
+ return file
475
+ end
476
+
477
+ PathRemoveFileSpec(file)
478
+ file = file.split(0.chr).first
479
+ PathRemoveBackslash(file)
480
+
405
481
  file.tr!("\\", '/') if fpath
406
- return file
482
+ file
407
483
  end
408
-
409
- PathStripPath(file) # Gives us the basename
410
-
411
- if suffix
412
- if suffix == '.*'
413
- PathRemoveExtension(file)
414
- else
415
- if PathFindExtension(file) == suffix
416
- PathRemoveExtension(file)
417
- end
484
+
485
+ # Returns +file+ in long format. For example, if 'SOMEFI~1.TXT'
486
+ # was the argument provided, and the short representation for
487
+ # 'somefile.txt', then this method would return 'somefile.txt'.
488
+ #
489
+ # Note that certain file system optimizations may prevent this method
490
+ # from working as expected. In that case, you will get back the file
491
+ # name in 8.3 format.
492
+ #
493
+ def long_path(file)
494
+ buf = 0.chr * MAX_PATH
495
+ if GetLongPathName(file, buf, buf.size) == 0
496
+ raise ArgumentError, get_last_error
418
497
  end
498
+ File.basename(buf.split(0.chr).first.strip)
419
499
  end
420
-
421
- file = file.split(0.chr).first
422
-
423
- # Trim trailing slashes
424
- while file[-1].chr == "\\"
425
- file.chop!
500
+
501
+ # Returns 'file_name' in 8.3 format. For example, 'c:\documentation.doc'
502
+ # would be returned as 'c:\docume~1.doc'.
503
+ #
504
+ def short_path(file)
505
+ buf = 0.chr * MAX_PATH
506
+ if GetShortPathName(file, buf, buf.size) == 0
507
+ raise ArgumentError, get_last_error
508
+ end
509
+ File.basename(buf.split(0.chr).first.strip)
426
510
  end
427
-
428
- # Return forward slashes if that's how the path was passed in.
429
- if fpath
430
- file.tr!("\\", '/')
511
+
512
+ # Splits the given string into a directory and a file component and
513
+ # returns them in a two element array. This was reimplemented because
514
+ # the current version does not handle UNC paths properly.
515
+ #
516
+ def split(file)
517
+ array = []
518
+
519
+ if file.empty? || PathIsRoot(file)
520
+ array.push(file, '')
521
+ else
522
+ array.push(File.dirname(file), File.basename(file))
523
+ end
524
+ array
431
525
  end
432
-
433
- file
434
- end
435
526
 
436
- # Returns all components of the filename given in +filename+ except the
437
- # last one.
438
- #
439
- # This was reimplemented because the current version does not handle UNC
440
- # paths properly, i.e. it should not return anything less than the root.
441
- # In all other respects it is identical to the current implementation.
442
- #
443
- # File.dirname("C:\\foo\\bar\\baz.txt") -> "C:\\foo\\bar"
444
- # File.dirname("\\\\foo\\bar") -> "\\\\foo\\bar"
445
- #
446
- def self.dirname(file)
447
- fpath = false
448
- file = file.dup
527
+ ## Stat methods
449
528
 
450
- if file.include?('/')
451
- file.tr!('/', "\\")
452
- fpath = true
529
+ # Returns a File::Stat object, as defined in the win32-file-stat package.
530
+ #
531
+ def stat(file)
532
+ File::Stat.new(file)
453
533
  end
454
534
 
455
- if PathIsRelative(file)
456
- return '.'
535
+ # Identical to File.stat on Windows.
536
+ #
537
+ def lstat(file)
538
+ File::Stat.new(file)
457
539
  end
458
540
 
459
- if PathIsRoot(file)
460
- file.tr!("\\", '/') if fpath
461
- return file
541
+ # Returns the file system's block size.
542
+ #
543
+ def blksize(file)
544
+ File::Stat.new(file).blksize
462
545
  end
463
-
464
- PathRemoveFileSpec(file)
465
- file = file.split(0.chr).first
466
- PathRemoveBackslash(file)
467
-
468
- file.tr!("\\", '/') if fpath
469
- file
470
- end
471
546
 
472
- # Returns +file+ in long format. For example, if 'SOMEFI~1.TXT'
473
- # was the argument provided, and the short representation for
474
- # 'somefile.txt', then this method would return 'somefile.txt'.
475
- #
476
- # Note that certain file system optimizations may prevent this method
477
- # from working as expected. In that case, you will get back the file
478
- # name in 8.3 format.
479
- #
480
- def self.long_path(file)
481
- buf = 0.chr * MAX_PATH
482
- if GetLongPathName(file, buf, buf.size) == 0
483
- raise ArgumentError, get_last_error
547
+ # Returns whether or not +file+ is a block device. For MS Windows this
548
+ # means a removable drive, cdrom or ramdisk.
549
+ #
550
+ def blockdev?(file)
551
+ File::Stat.new(file).blockdev?
484
552
  end
485
- File.basename(buf.split(0.chr).first.strip)
486
- end
487
553
 
488
- # Returns 'file_name' in 8.3 format. For example, 'c:\documentation.doc'
489
- # would be returned as 'c:\docume~1.doc'.
490
- #
491
- def self.short_path(file)
492
- buf = 0.chr * MAX_PATH
493
- if GetShortPathName(file, buf, buf.size) == 0
494
- raise ArgumentError, get_last_error
554
+ # Returns true if the file is a character device. This replaces the
555
+ # current Ruby implementation which always returns false.
556
+ #
557
+ def chardev?(file)
558
+ File::Stat.new(file).chardev?
495
559
  end
496
- File.basename(buf.split(0.chr).first.strip)
497
- end
498
560
 
499
- # Splits the given string into a directory and a file component and returns
500
- # them in a two element array. This was reimplemented because the
501
- # current version does not handle UNC paths properly.
502
- #
503
- def self.split(file)
504
- array = []
505
-
506
- if file.empty? || PathIsRoot(file)
507
- array.push(file, '')
508
- else
509
- array.push(File.dirname(file), File.basename(file))
561
+ # Returns the size of the file in bytes.
562
+ #
563
+ # This was reimplemented because the current version does not handle file
564
+ # sizes greater than 2gb.
565
+ #
566
+ def size(file)
567
+ File::Stat.new(file).size
510
568
  end
511
- array
512
- end
513
-
514
- ## Stat methods
515
-
516
- # Returns a File::Stat object, as defined in the win32-file-stat package.
517
- #
518
- def self.stat(file)
519
- File::Stat.new(file)
520
- end
521
-
522
- # Identical to File.stat on Windows.
523
- #
524
- def self.lstat(file)
525
- File::Stat.new(file)
526
- end
527
-
528
- # Returns the file system's block size.
529
- #
530
- def self.blksize(file)
531
- File::Stat.new(file).blksize
532
- end
533
-
534
- # Returns whether or not +file+ is a block device.
535
- #
536
- def self.blockdev?(file)
537
- File::Stat.new(file).blockdev?
538
- end
539
-
540
- # Returns true if the file is a character device. This replaces the current
541
- # Ruby implementation which always returns false.
542
- #
543
- def self.chardev?(file)
544
- File::Stat.new(file).chardev?
545
- end
546
569
 
547
- # Returns the size of the file in bytes.
548
- #
549
- # This was reimplemented because the current version does not handle file
550
- # sizes greater than 2gb.
551
- #
552
- def self.size(file)
553
- File::Stat.new(file).size
554
- end
570
+ # We no longer need the aliases, so remove them
571
+ remove_method(:basename_orig, :blockdev_orig, :chardev_orig)
572
+ remove_method(:dirname_orig, :lstat_orig, :size_orig)
573
+ remove_method(:split_orig, :stat_orig)
574
+ end # class << self
555
575
 
556
576
  ## Attribute methods
557
577
 
@@ -923,7 +943,7 @@ class File
923
943
  #
924
944
  def sparse=(bool)
925
945
  unless bool
926
- warn 'Cannot remove sparse property from a file - operation ignored'
946
+ warn 'cannot remove sparse property from a file - operation ignored'
927
947
  return
928
948
  end
929
949
 
@@ -1019,19 +1039,4 @@ class File
1019
1039
  alias :set_attr :set_attributes
1020
1040
  alias :unset_attr :remove_attributes
1021
1041
  end
1022
-
1023
- private
1024
-
1025
- # This is based on the CTL_CODE macro in WinIoCtl.h
1026
- def CTL_CODE(device, function, method, access)
1027
- ((device) << 16) | ((access) << 14) | ((function) << 2) | (method)
1028
- end
1029
-
1030
- def FSCTL_SET_COMPRESSION
1031
- CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 16, 0, FILE_READ_DATA | FILE_WRITE_DATA)
1032
- end
1033
-
1034
- def FSCTL_SET_SPARSE
1035
- CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 49, 0, FILE_SPECIAL_ACCESS)
1036
- end
1037
1042
  end