win32-file 0.5.3 → 0.5.4

Sign up to get free protection for your applications and to get access to all the features.
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