win32-file-stat 1.3.4 → 1.3.5
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 +7 -1
- data/Rakefile +19 -17
- data/lib/win32/file/stat.rb +698 -689
- data/test/test_file_stat.rb +1 -1
- data/win32-file-stat.gemspec +22 -28
- metadata +37 -17
data/CHANGES
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
== 1.3.5 - 21-Nov-2011
|
2
|
+
* Fixed a bug in the dev method for 1.9.x.
|
3
|
+
* Fixed some method redefinition warnings.
|
4
|
+
* Refactored the Rakefile and gemspec. The old install task has been
|
5
|
+
removed from the Rakefile.
|
6
|
+
|
1
7
|
== 1.3.4 - 13-Aug-2009
|
2
8
|
* Changed license to Artistic 2.0.
|
3
9
|
* Some gemspec updates, including the addition of a license, an updated
|
@@ -91,4 +97,4 @@
|
|
91
97
|
* Some optimization in the constructor.
|
92
98
|
|
93
99
|
== 1.0.0 - 13-Apr-2006
|
94
|
-
* Initial release
|
100
|
+
* Initial release
|
data/Rakefile
CHANGED
@@ -1,24 +1,26 @@
|
|
1
1
|
require 'rake'
|
2
|
+
require 'rake/clean'
|
2
3
|
require 'rake/testtask'
|
3
|
-
require 'rbconfig'
|
4
|
-
include Config
|
5
4
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
5
|
+
CLEAN.include("**/*.gem", "**/*.rbx", "**/*.rbc")
|
6
|
+
|
7
|
+
namespace :gem do
|
8
|
+
desc "Create the win32-file-stat gem"
|
9
|
+
task :create => [:clean] do
|
10
|
+
spec = eval(IO.read("win32-file-stat.gemspec"))
|
11
|
+
Gem::Builder.new(spec).build
|
12
|
+
end
|
13
13
|
|
14
|
-
desc
|
15
|
-
task :
|
16
|
-
|
17
|
-
|
18
|
-
|
14
|
+
desc "Install the win32-file-stat gem"
|
15
|
+
task :install => [:create] do
|
16
|
+
file = Dir["win32-file-stat*.gem"].first
|
17
|
+
sh "gem install #{file}"
|
18
|
+
end
|
19
19
|
end
|
20
20
|
|
21
21
|
Rake::TestTask.new do |t|
|
22
|
-
|
23
|
-
|
24
|
-
end
|
22
|
+
t.verbose = true
|
23
|
+
t.warning = true
|
24
|
+
end
|
25
|
+
|
26
|
+
task :default => :test
|
data/lib/win32/file/stat.rb
CHANGED
@@ -14,727 +14,736 @@ require 'windows/ntfs/winternl'
|
|
14
14
|
require 'pp'
|
15
15
|
|
16
16
|
class File::Stat
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
17
|
+
include Windows::MSVCRT::Buffer
|
18
|
+
include Windows::MSVCRT::File
|
19
|
+
include Windows::DeviceIO
|
20
|
+
include Windows::FileSystem
|
21
|
+
include Windows::Path
|
22
|
+
include Windows::File
|
23
|
+
include Windows::Error
|
24
|
+
include Windows::Handle
|
25
|
+
include Windows::Volume
|
26
|
+
include Windows::Process
|
27
|
+
include Windows::Security
|
28
|
+
include Windows::Time
|
29
|
+
include Windows::NTFS::Winternl
|
30
|
+
include Comparable
|
31
|
+
|
32
|
+
# The version of the win32-file-stat library
|
33
|
+
VERSION = '1.3.5'
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
# :stopdoc:
|
38
|
+
|
39
|
+
# Defined in Ruby's win32.h. Not meant for public consumption.
|
40
|
+
S_IWGRP = 0020
|
41
|
+
S_IWOTH = 0002
|
42
|
+
|
43
|
+
# This is the only way to avoid a -w warning for initialize. We remove
|
44
|
+
# it later, after we've defined our initialize method.
|
45
|
+
alias old_init initialize
|
46
|
+
|
47
|
+
# Make this library -w clean
|
48
|
+
undef_method(:atime, :blksize, :blockdev?, :blocks, :chardev?, :ctime)
|
49
|
+
undef_method(:dev, :directory?, :executable?, :file?, :ftype, :gid, :ino)
|
50
|
+
undef_method(:executable_real?, :grpowned?, :mode, :mtime, :nlink, :owned?)
|
51
|
+
undef_method(:pipe?, :readable?, :rdev, :readable_real?, :setgid?, :setuid?)
|
52
|
+
undef_method(:size, :size?, :socket?, :sticky?, :symlink?, :uid, :writable?)
|
53
|
+
undef_method(:dev_major, :dev_minor, :rdev_major, :rdev_minor)
|
54
|
+
undef_method(:writable_real?, :zero?)
|
55
|
+
undef_method(:pretty_print, :inspect, :<=>)
|
56
|
+
|
57
|
+
public
|
58
|
+
|
59
|
+
# Always nil. Provided for interface compatibility only.
|
60
|
+
attr_reader :dev_major
|
61
|
+
attr_reader :dev_minor
|
62
|
+
attr_reader :rdev_major
|
63
|
+
attr_reader :rdev_minor
|
64
|
+
|
65
|
+
# :startdoc:
|
66
|
+
|
67
|
+
# Creates and returns a File::Stat object, which encapsulate common status
|
68
|
+
# information for File objects on MS Windows sytems. The information is
|
69
|
+
# recorded at the moment the File::Stat object is created; changes made to
|
70
|
+
# the file after that point will not be reflected.
|
71
|
+
#
|
72
|
+
def initialize(file)
|
73
|
+
@file = File.expand_path(file)
|
74
|
+
@file = @file.tr('/', "\\")
|
75
|
+
@file = multi_to_wide(@file)
|
76
|
+
|
77
|
+
@file_type = get_file_type(@file)
|
78
|
+
@chardev = @file_type == FILE_TYPE_CHAR
|
79
|
+
|
80
|
+
case GetDriveTypeW(@file)
|
81
|
+
when DRIVE_REMOVABLE, DRIVE_CDROM, DRIVE_RAMDISK
|
82
|
+
@blockdev = true
|
83
|
+
else
|
84
|
+
@blockdev = false
|
85
|
+
end
|
85
86
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
87
|
+
# The stat struct in stat.h only has 11 members on Windows
|
88
|
+
stat_buf = [0,0,0,0,0,0,0,0,0,0,0].pack('ISSsssIQQQQ')
|
89
|
+
|
90
|
+
# The stat64 function doesn't seem to like character devices
|
91
|
+
if wstat64(@file, stat_buf) != 0
|
92
|
+
raise ArgumentError, get_last_error unless @chardev
|
93
|
+
end
|
94
|
+
|
95
|
+
# Some bytes skipped (padding for struct alignment)
|
96
|
+
@dev = stat_buf[0, 4].unpack('I').first # Drive number
|
97
|
+
@ino = stat_buf[4, 2].unpack('S').first # Meaningless
|
98
|
+
@mode = stat_buf[6, 2].unpack('S').first # File mode bit mask
|
99
|
+
@nlink = stat_buf[8, 2].unpack('s').first # Always 1
|
100
|
+
@uid = stat_buf[10, 2].unpack('s').first # Always 0
|
101
|
+
@gid = stat_buf[12, 2].unpack('s').first # Always 0
|
102
|
+
@rdev = stat_buf[16, 4].unpack('I').first # Same as dev
|
103
|
+
@size = stat_buf[24, 8].unpack('Q').first # Size of file in bytes
|
103
104
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
105
|
+
# This portion can fail in rare, FS related instances. If it does, set
|
106
|
+
# the various times to Time.at(0).
|
107
|
+
begin
|
108
|
+
@atime = Time.at(stat_buf[32, 8].unpack('Q').first) # Access time
|
109
|
+
@mtime = Time.at(stat_buf[40, 8].unpack('Q').first) # Mod time
|
110
|
+
@ctime = Time.at(stat_buf[48, 8].unpack('Q').first) # Creation time
|
111
|
+
rescue
|
112
|
+
@atime = Time.at(0)
|
113
|
+
@mtime = Time.at(0)
|
114
|
+
@ctime = Time.at(0)
|
115
|
+
end
|
115
116
|
|
116
|
-
|
117
|
+
@mode = 33188 if @chardev
|
117
118
|
|
118
|
-
|
119
|
-
|
119
|
+
attributes = GetFileAttributesW(@file)
|
120
|
+
error_num = GetLastError()
|
120
121
|
|
121
|
-
|
122
|
-
|
123
|
-
|
122
|
+
# Locked files.
|
123
|
+
if error_num == ERROR_SHARING_VIOLATION
|
124
|
+
buffer = 0.chr * 512
|
124
125
|
|
125
|
-
|
126
|
-
|
126
|
+
begin
|
127
|
+
handle = FindFirstFileW(@file, buffer)
|
127
128
|
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
end
|
134
|
-
|
135
|
-
attributes = buffer[0,4].unpack('L').first
|
136
|
-
st = 0.chr * 16
|
137
|
-
FileTimeToSystemTime(buffer[4,8],st)
|
138
|
-
y,m,w,d,h,n,s,i = st.unpack('SSSSSSSS')
|
139
|
-
@ctime = Time.local(y,m,d,h,n,s)
|
140
|
-
|
141
|
-
st = 0.chr * 16
|
142
|
-
FileTimeToSystemTime(buffer[12,8],st)
|
143
|
-
y,m,w,d,h,n,s,i = st.unpack('SSSSSSSS')
|
144
|
-
@atime = Time.local(y,m,d,h,n,s)
|
145
|
-
|
146
|
-
st = 0.chr * 16
|
147
|
-
FileTimeToSystemTime(buffer[20,8],st)
|
148
|
-
y,m,w,d,h,n,s,i = st.unpack('SSSSSSSS')
|
149
|
-
@mtime = Time.local(y,m,d,h,n,s)
|
129
|
+
if handle == INVALID_HANDLE_VALUE
|
130
|
+
raise SystemCallError, get_last_error()
|
131
|
+
end
|
132
|
+
ensure
|
133
|
+
FindClose(handle) if handle != INVALID_HANDLE_VALUE
|
150
134
|
end
|
151
135
|
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
136
|
+
attributes = buffer[0,4].unpack('L').first
|
137
|
+
st = 0.chr * 16
|
138
|
+
FileTimeToSystemTime(buffer[4,8],st)
|
139
|
+
y,m,w,d,h,n,s,i = st.unpack('SSSSSSSS')
|
140
|
+
@ctime = Time.local(y,m,d,h,n,s)
|
141
|
+
|
142
|
+
st = 0.chr * 16
|
143
|
+
FileTimeToSystemTime(buffer[12,8],st)
|
144
|
+
y,m,w,d,h,n,s,i = st.unpack('SSSSSSSS')
|
145
|
+
@atime = Time.local(y,m,d,h,n,s)
|
146
|
+
|
147
|
+
st = 0.chr * 16
|
148
|
+
FileTimeToSystemTime(buffer[20,8],st)
|
149
|
+
y,m,w,d,h,n,s,i = st.unpack('SSSSSSSS')
|
150
|
+
@mtime = Time.local(y,m,d,h,n,s)
|
151
|
+
end
|
152
|
+
|
153
|
+
# Ignore errors caused by empty/open/used block devices.
|
154
|
+
if attributes == INVALID_FILE_ATTRIBUTES
|
155
|
+
unless error_num == ERROR_NOT_READY
|
156
|
+
raise ArgumentError, get_last_error(error_num)
|
157
157
|
end
|
158
|
+
end
|
158
159
|
|
159
|
-
|
160
|
+
@blksize = get_blksize(@file)
|
160
161
|
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
162
|
+
# This is a reasonable guess
|
163
|
+
case @blksize
|
164
|
+
when nil
|
165
|
+
@blocks = nil
|
166
|
+
when 0
|
167
|
+
@blocks = 0
|
168
|
+
else
|
169
|
+
@blocks = (@size.to_f / @blksize.to_f).ceil
|
170
|
+
end
|
170
171
|
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
172
|
+
@readonly = attributes & FILE_ATTRIBUTE_READONLY > 0
|
173
|
+
@hidden = attributes & FILE_ATTRIBUTE_HIDDEN > 0
|
174
|
+
@system = attributes & FILE_ATTRIBUTE_SYSTEM > 0
|
175
|
+
@archive = attributes & FILE_ATTRIBUTE_ARCHIVE > 0
|
176
|
+
@directory = attributes & FILE_ATTRIBUTE_DIRECTORY > 0
|
177
|
+
@encrypted = attributes & FILE_ATTRIBUTE_ENCRYPTED > 0
|
178
|
+
@normal = attributes & FILE_ATTRIBUTE_NORMAL > 0
|
179
|
+
@temporary = attributes & FILE_ATTRIBUTE_TEMPORARY > 0
|
180
|
+
@sparse = attributes & FILE_ATTRIBUTE_SPARSE_FILE > 0
|
181
|
+
@reparse_point = attributes & FILE_ATTRIBUTE_REPARSE_POINT > 0
|
182
|
+
@compressed = attributes & FILE_ATTRIBUTE_COMPRESSED > 0
|
183
|
+
@offline = attributes & FILE_ATTRIBUTE_OFFLINE > 0
|
184
|
+
@indexed = attributes & ~FILE_ATTRIBUTE_NOT_CONTENT_INDEXED > 0
|
184
185
|
|
185
|
-
|
186
|
-
|
187
|
-
|
186
|
+
@executable = GetBinaryTypeW(@file, '')
|
187
|
+
@regular = @file_type == FILE_TYPE_DISK
|
188
|
+
@pipe = @file_type == FILE_TYPE_PIPE
|
188
189
|
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
190
|
+
# Not supported and/or meaningless
|
191
|
+
@dev_major = nil
|
192
|
+
@dev_minor = nil
|
193
|
+
@grpowned = true
|
194
|
+
@owned = true
|
195
|
+
@readable = true
|
196
|
+
@readable_real = true
|
197
|
+
@rdev_major = nil
|
198
|
+
@rdev_minor = nil
|
199
|
+
@setgid = false
|
200
|
+
@setuid = false
|
201
|
+
@sticky = false
|
202
|
+
@symlink = false
|
203
|
+
@writable = true
|
204
|
+
@writable_real = true
|
205
|
+
end
|
206
|
+
|
207
|
+
## Comparable
|
208
|
+
|
209
|
+
# Compares two File::Stat objects. Comparsion is based on mtime only.
|
210
|
+
#
|
211
|
+
def <=>(other)
|
212
|
+
@mtime.to_i <=> other.mtime.to_i
|
213
|
+
end
|
214
|
+
|
215
|
+
## Miscellaneous
|
216
|
+
|
217
|
+
# Returns whether or not the file is a block device. For MS Windows a
|
218
|
+
# block device is a removable drive, cdrom or ramdisk.
|
219
|
+
#
|
220
|
+
def blockdev?
|
221
|
+
@blockdev
|
222
|
+
end
|
223
|
+
|
224
|
+
# Returns whether or not the file is a character device.
|
225
|
+
#
|
226
|
+
def chardev?
|
227
|
+
@chardev
|
228
|
+
end
|
229
|
+
|
230
|
+
# Returns whether or not the file is executable. Generally speaking, this
|
231
|
+
# means .bat, .cmd, .com, and .exe files.
|
232
|
+
#
|
233
|
+
def executable?
|
234
|
+
@executable
|
235
|
+
end
|
236
|
+
|
237
|
+
alias :executable_real? :executable?
|
238
|
+
|
239
|
+
# Returns whether or not the file is a regular file, as opposed to a pipe,
|
240
|
+
# socket, etc.
|
241
|
+
#
|
242
|
+
def file?
|
243
|
+
@regular
|
244
|
+
end
|
245
|
+
|
246
|
+
# Identifies the type of file. The return string is one of 'file',
|
247
|
+
# 'directory', 'characterSpecial', 'socket' or 'unknown'.
|
248
|
+
#
|
249
|
+
def ftype
|
250
|
+
return 'directory' if directory?
|
251
|
+
case @file_type
|
252
|
+
when FILE_TYPE_CHAR
|
253
|
+
'characterSpecial'
|
254
|
+
when FILE_TYPE_DISK
|
255
|
+
'file'
|
256
|
+
when FILE_TYPE_PIPE
|
257
|
+
'socket'
|
258
|
+
else
|
259
|
+
if blockdev?
|
260
|
+
'blockSpecial'
|
261
|
+
else
|
262
|
+
'unknown'
|
263
|
+
end
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
# Meaningless on Windows.
|
268
|
+
#
|
269
|
+
def grpowned?
|
270
|
+
@grpowned
|
271
|
+
end
|
272
|
+
|
273
|
+
# Always true on Windows
|
274
|
+
def owned?
|
275
|
+
@owned
|
276
|
+
end
|
277
|
+
|
278
|
+
# Returns whether or not the file is a pipe.
|
279
|
+
#
|
280
|
+
def pipe?
|
281
|
+
@pipe
|
282
|
+
end
|
283
|
+
|
284
|
+
alias :socket? :pipe?
|
285
|
+
|
286
|
+
# Meaningless on Windows
|
287
|
+
#
|
288
|
+
def readable?
|
289
|
+
@readable
|
290
|
+
end
|
291
|
+
|
292
|
+
# Meaningless on Windows
|
293
|
+
#
|
294
|
+
def readable_real?
|
295
|
+
@readable_real
|
296
|
+
end
|
297
|
+
|
298
|
+
# Meaningless on Windows
|
299
|
+
#
|
300
|
+
def setgid?
|
301
|
+
@setgid
|
302
|
+
end
|
303
|
+
|
304
|
+
# Meaningless on Windows
|
305
|
+
#
|
306
|
+
def setuid?
|
307
|
+
@setuid
|
308
|
+
end
|
309
|
+
|
310
|
+
# Returns nil if statfile is a zero-length file; otherwise, returns the
|
311
|
+
# file size. Usable as a condition in tests.
|
312
|
+
#
|
313
|
+
def size?
|
314
|
+
@size > 0 ? @size : nil
|
315
|
+
end
|
316
|
+
|
317
|
+
# Meaningless on Windows.
|
318
|
+
#
|
319
|
+
def sticky?
|
320
|
+
@sticky
|
321
|
+
end
|
322
|
+
|
323
|
+
# Meaningless on Windows at the moment. This may change in the future.
|
324
|
+
#
|
325
|
+
def symlink?
|
326
|
+
@symlink
|
327
|
+
end
|
328
|
+
|
329
|
+
# Meaningless on Windows.
|
330
|
+
#
|
331
|
+
def writable?
|
332
|
+
@writable
|
333
|
+
end
|
334
|
+
|
335
|
+
# Meaningless on Windows.
|
336
|
+
#
|
337
|
+
def writable_real?
|
338
|
+
@writable_real
|
339
|
+
end
|
340
|
+
|
341
|
+
# Returns whether or not the file size is zero.
|
342
|
+
#
|
343
|
+
def zero?
|
344
|
+
@size == 0
|
345
|
+
end
|
346
|
+
|
347
|
+
## Attribute members
|
348
|
+
|
349
|
+
# Returns whether or not the file is an archive file.
|
350
|
+
#
|
351
|
+
def archive?
|
352
|
+
@archive
|
353
|
+
end
|
354
|
+
|
355
|
+
# Returns whether or not the file is compressed.
|
356
|
+
#
|
357
|
+
def compressed?
|
358
|
+
@compressed
|
359
|
+
end
|
360
|
+
|
361
|
+
# Returns whether or not the file is a directory.
|
362
|
+
#
|
363
|
+
def directory?
|
364
|
+
@directory
|
365
|
+
end
|
366
|
+
|
367
|
+
# Returns whether or not the file in encrypted.
|
368
|
+
#
|
369
|
+
def encrypted?
|
370
|
+
@encrypted
|
371
|
+
end
|
372
|
+
|
373
|
+
# Returns whether or not the file is hidden.
|
374
|
+
#
|
375
|
+
def hidden?
|
376
|
+
@hidden
|
377
|
+
end
|
378
|
+
|
379
|
+
# Returns whether or not the file is content indexed.
|
380
|
+
#
|
381
|
+
def indexed?
|
382
|
+
@indexed
|
383
|
+
end
|
384
|
+
|
385
|
+
alias :content_indexed? :indexed?
|
386
|
+
|
387
|
+
# Returns whether or not the file is 'normal'. This is only true if
|
388
|
+
# virtually all other attributes are false.
|
389
|
+
#
|
390
|
+
def normal?
|
391
|
+
@normal
|
392
|
+
end
|
393
|
+
|
394
|
+
# Returns whether or not the file is offline.
|
395
|
+
#
|
396
|
+
def offline?
|
397
|
+
@offline
|
398
|
+
end
|
399
|
+
|
400
|
+
# Returns whether or not the file is readonly.
|
401
|
+
#
|
402
|
+
def readonly?
|
403
|
+
@readonly
|
404
|
+
end
|
405
|
+
|
406
|
+
alias :read_only? :readonly?
|
407
|
+
|
408
|
+
# Returns whether or not the file is a reparse point.
|
409
|
+
#
|
410
|
+
def reparse_point?
|
411
|
+
@reparse_point
|
412
|
+
end
|
413
|
+
|
414
|
+
# Returns whether or not the file is a sparse file. In most cases a sparse
|
415
|
+
# file is an image file.
|
416
|
+
#
|
417
|
+
def sparse?
|
418
|
+
@sparse
|
419
|
+
end
|
420
|
+
|
421
|
+
# Returns whether or not the file is a system file.
|
422
|
+
#
|
423
|
+
def system?
|
424
|
+
@system
|
425
|
+
end
|
426
|
+
|
427
|
+
# Returns whether or not the file is being used for temporary storage.
|
428
|
+
#
|
429
|
+
def temporary?
|
430
|
+
@temporary
|
431
|
+
end
|
432
|
+
|
433
|
+
## Standard stat members
|
434
|
+
|
435
|
+
# Returns a Time object containing the last access time.
|
436
|
+
#
|
437
|
+
def atime
|
438
|
+
@atime
|
439
|
+
end
|
440
|
+
|
441
|
+
# Returns the file system's block size, or nil if it cannot be determined.
|
442
|
+
#
|
443
|
+
def blksize
|
444
|
+
@blksize
|
445
|
+
end
|
446
|
+
|
447
|
+
# Returns the number of blocks used by the file, where a block is defined
|
448
|
+
# as size divided by blksize, rounded up.
|
449
|
+
#
|
450
|
+
#--
|
451
|
+
# This is a fudge. A search of the internet reveals different ways people
|
452
|
+
# have defined st_blocks on MS Windows.
|
453
|
+
#
|
454
|
+
def blocks
|
455
|
+
@blocks
|
456
|
+
end
|
457
|
+
|
458
|
+
# Returns a Time object containing the time that the file status associated
|
459
|
+
# with the file was changed.
|
460
|
+
#
|
461
|
+
def ctime
|
462
|
+
@ctime
|
463
|
+
end
|
464
|
+
|
465
|
+
# Drive letter (A-Z) of the disk containing the file. If the path is a
|
466
|
+
# UNC path then the drive number (probably -1) is returned instead.
|
467
|
+
#
|
468
|
+
def dev
|
469
|
+
if PathIsUNCW(@file)
|
470
|
+
@dev
|
471
|
+
else
|
472
|
+
if RUBY_VERSION.to_f >= 1.9
|
473
|
+
(@dev + 'A'.ord).chr + ':'
|
474
|
+
else
|
475
|
+
(@dev + ?A).chr + ':'
|
263
476
|
end
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
def writable_real?
|
337
|
-
@writable_real
|
338
|
-
end
|
339
|
-
|
340
|
-
# Returns whether or not the file size is zero.
|
341
|
-
#
|
342
|
-
def zero?
|
343
|
-
@size == 0
|
344
|
-
end
|
345
|
-
|
346
|
-
## Attribute members
|
347
|
-
|
348
|
-
# Returns whether or not the file is an archive file.
|
349
|
-
#
|
350
|
-
def archive?
|
351
|
-
@archive
|
352
|
-
end
|
353
|
-
|
354
|
-
# Returns whether or not the file is compressed.
|
355
|
-
#
|
356
|
-
def compressed?
|
357
|
-
@compressed
|
358
|
-
end
|
359
|
-
|
360
|
-
# Returns whether or not the file is a directory.
|
361
|
-
#
|
362
|
-
def directory?
|
363
|
-
@directory
|
364
|
-
end
|
365
|
-
|
366
|
-
# Returns whether or not the file in encrypted.
|
367
|
-
#
|
368
|
-
def encrypted?
|
369
|
-
@encrypted
|
370
|
-
end
|
371
|
-
|
372
|
-
# Returns whether or not the file is hidden.
|
373
|
-
#
|
374
|
-
def hidden?
|
375
|
-
@hidden
|
376
|
-
end
|
377
|
-
|
378
|
-
# Returns whether or not the file is content indexed.
|
379
|
-
#
|
380
|
-
def indexed?
|
381
|
-
@indexed
|
382
|
-
end
|
383
|
-
alias :content_indexed? :indexed?
|
384
|
-
|
385
|
-
# Returns whether or not the file is 'normal'. This is only true if
|
386
|
-
# virtually all other attributes are false.
|
387
|
-
#
|
388
|
-
def normal?
|
389
|
-
@normal
|
390
|
-
end
|
391
|
-
|
392
|
-
# Returns whether or not the file is offline.
|
393
|
-
#
|
394
|
-
def offline?
|
395
|
-
@offline
|
396
|
-
end
|
397
|
-
|
398
|
-
# Returns whether or not the file is readonly.
|
399
|
-
#
|
400
|
-
def readonly?
|
401
|
-
@readonly
|
402
|
-
end
|
403
|
-
|
404
|
-
alias :read_only? :readonly?
|
405
|
-
|
406
|
-
# Returns whether or not the file is a reparse point.
|
407
|
-
#
|
408
|
-
def reparse_point?
|
409
|
-
@reparse_point
|
410
|
-
end
|
411
|
-
|
412
|
-
# Returns whether or not the file is a sparse file. In most cases a sparse
|
413
|
-
# file is an image file.
|
414
|
-
#
|
415
|
-
def sparse?
|
416
|
-
@sparse
|
417
|
-
end
|
418
|
-
|
419
|
-
# Returns whether or not the file is a system file.
|
420
|
-
#
|
421
|
-
def system?
|
422
|
-
@system
|
423
|
-
end
|
424
|
-
|
425
|
-
# Returns whether or not the file is being used for temporary storage.
|
426
|
-
#
|
427
|
-
def temporary?
|
428
|
-
@temporary
|
429
|
-
end
|
430
|
-
|
431
|
-
## Standard stat members
|
432
|
-
|
433
|
-
# Returns a Time object containing the last access time.
|
434
|
-
#
|
435
|
-
def atime
|
436
|
-
@atime
|
437
|
-
end
|
438
|
-
|
439
|
-
# Returns the file system's block size, or nil if it cannot be determined.
|
440
|
-
#
|
441
|
-
def blksize
|
442
|
-
@blksize
|
443
|
-
end
|
444
|
-
|
445
|
-
# Returns the number of blocks used by the file, where a block is defined
|
446
|
-
# as size divided by blksize, rounded up.
|
447
|
-
#
|
448
|
-
#--
|
449
|
-
# This is a fudge. A search of the internet reveals different ways people
|
450
|
-
# have defined st_blocks on MS Windows.
|
451
|
-
#
|
452
|
-
def blocks
|
453
|
-
@blocks
|
454
|
-
end
|
455
|
-
|
456
|
-
# Returns a Time object containing the time that the file status associated
|
457
|
-
# with the file was changed.
|
458
|
-
#
|
459
|
-
def ctime
|
460
|
-
@ctime
|
461
|
-
end
|
462
|
-
|
463
|
-
# Drive letter (A-Z) of the disk containing the file. If the path is a
|
464
|
-
# UNC path then the drive number (probably -1) is returned instead.
|
465
|
-
#
|
466
|
-
def dev
|
467
|
-
if PathIsUNCW(@file)
|
468
|
-
@dev
|
477
|
+
end
|
478
|
+
end
|
479
|
+
|
480
|
+
# Group ID. Always 0.
|
481
|
+
#
|
482
|
+
def gid
|
483
|
+
@gid
|
484
|
+
end
|
485
|
+
|
486
|
+
# Inode number. Meaningless on NTFS.
|
487
|
+
#
|
488
|
+
def ino
|
489
|
+
@ino
|
490
|
+
end
|
491
|
+
|
492
|
+
# Bit mask for file-mode information.
|
493
|
+
#
|
494
|
+
# :no-doc:
|
495
|
+
# This was taken from rb_win32_stat() in win32.c. I'm not entirely
|
496
|
+
# sure what the point is.
|
497
|
+
#
|
498
|
+
def mode
|
499
|
+
@mode &= ~(S_IWGRP | S_IWOTH)
|
500
|
+
end
|
501
|
+
|
502
|
+
# Returns a Time object containing the modification time.
|
503
|
+
#
|
504
|
+
def mtime
|
505
|
+
@mtime
|
506
|
+
end
|
507
|
+
|
508
|
+
# Drive number of the disk containing the file.
|
509
|
+
#
|
510
|
+
def rdev
|
511
|
+
@rdev
|
512
|
+
end
|
513
|
+
|
514
|
+
# Always 1
|
515
|
+
#
|
516
|
+
def nlink
|
517
|
+
@nlink
|
518
|
+
end
|
519
|
+
|
520
|
+
# Returns the size of the file, in bytes.
|
521
|
+
#
|
522
|
+
def size
|
523
|
+
@size
|
524
|
+
end
|
525
|
+
|
526
|
+
# User ID. Always 0.
|
527
|
+
#
|
528
|
+
def uid
|
529
|
+
@uid
|
530
|
+
end
|
531
|
+
|
532
|
+
# Returns a stringified version of a File::Stat object.
|
533
|
+
#
|
534
|
+
def inspect
|
535
|
+
members = %w[
|
536
|
+
archive? atime blksize blockdev? blocks compressed? ctime dev
|
537
|
+
encrypted? gid hidden? indexed? ino mode mtime rdev nlink normal?
|
538
|
+
offline? readonly? reparse_point? size sparse? system? temporary?
|
539
|
+
uid
|
540
|
+
]
|
541
|
+
|
542
|
+
str = "#<#{self.class}"
|
543
|
+
|
544
|
+
members.sort.each{ |mem|
|
545
|
+
if mem == 'mode'
|
546
|
+
str << " #{mem}=" << sprintf("0%o", send(mem.intern))
|
547
|
+
elsif mem[-1].chr == '?'
|
548
|
+
str << " #{mem.chop}=" << send(mem.intern).to_s
|
469
549
|
else
|
470
|
-
|
550
|
+
str << " #{mem}=" << send(mem.intern).to_s
|
471
551
|
end
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
end
|
507
|
-
|
508
|
-
# Always 1
|
509
|
-
#
|
510
|
-
def nlink
|
511
|
-
@nlink
|
512
|
-
end
|
513
|
-
|
514
|
-
# Returns the size of the file, in bytes.
|
515
|
-
#
|
516
|
-
def size
|
517
|
-
@size
|
518
|
-
end
|
519
|
-
|
520
|
-
# User ID. Always 0.
|
521
|
-
#
|
522
|
-
def uid
|
523
|
-
@uid
|
524
|
-
end
|
525
|
-
|
526
|
-
# Returns a stringified version of a File::Stat object.
|
527
|
-
#
|
528
|
-
def inspect
|
529
|
-
members = %w/
|
530
|
-
archive? atime blksize blockdev? blocks compressed? ctime dev
|
531
|
-
encrypted? gid hidden? indexed? ino mode mtime rdev nlink normal?
|
532
|
-
offline? readonly? reparse_point? size sparse? system? temporary?
|
533
|
-
uid
|
534
|
-
/
|
535
|
-
str = "#<#{self.class}"
|
536
|
-
members.sort.each{ |mem|
|
537
|
-
if mem == 'mode'
|
538
|
-
str << " #{mem}=" << sprintf("0%o", send(mem.intern))
|
539
|
-
elsif mem[-1].chr == '?'
|
540
|
-
str << " #{mem.chop}=" << send(mem.intern).to_s
|
541
|
-
else
|
542
|
-
str << " #{mem}=" << send(mem.intern).to_s
|
543
|
-
end
|
544
|
-
}
|
545
|
-
str
|
546
|
-
end
|
547
|
-
|
548
|
-
# A custom pretty print method. This was necessary not only to handle
|
549
|
-
# the additional attributes, but to work around an error caused by the
|
550
|
-
# builtin method for the current File::Stat class (see pp.rb).
|
551
|
-
#
|
552
|
-
def pretty_print(q)
|
553
|
-
members = %w/
|
554
|
-
archive? atime blksize blockdev? blocks compressed? ctime dev
|
555
|
-
encrypted? gid hidden? indexed? ino mode mtime rdev nlink normal?
|
556
|
-
offline? readonly? reparse_point? size sparse? system? temporary?
|
557
|
-
uid
|
558
|
-
/
|
559
|
-
|
560
|
-
q.object_group(self){
|
561
|
-
q.breakable
|
562
|
-
members.each{ |mem|
|
563
|
-
q.group{
|
564
|
-
q.text("#{mem}".ljust(15) + "=> ")
|
565
|
-
if mem == 'mode'
|
566
|
-
q.text(sprintf("0%o", send(mem.intern)))
|
567
|
-
else
|
568
|
-
val = self.send(mem.intern)
|
569
|
-
if val.nil?
|
570
|
-
q.text('nil')
|
571
|
-
else
|
572
|
-
q.text(val.to_s)
|
573
|
-
end
|
574
|
-
end
|
575
|
-
}
|
576
|
-
q.comma_breakable unless mem == members.last
|
577
|
-
}
|
552
|
+
}
|
553
|
+
|
554
|
+
str
|
555
|
+
end
|
556
|
+
|
557
|
+
# A custom pretty print method. This was necessary not only to handle
|
558
|
+
# the additional attributes, but to work around an error caused by the
|
559
|
+
# builtin method for the current File::Stat class (see pp.rb).
|
560
|
+
#
|
561
|
+
def pretty_print(q)
|
562
|
+
members = %w[
|
563
|
+
archive? atime blksize blockdev? blocks compressed? ctime dev
|
564
|
+
encrypted? gid hidden? indexed? ino mode mtime rdev nlink normal?
|
565
|
+
offline? readonly? reparse_point? size sparse? system? temporary?
|
566
|
+
uid
|
567
|
+
]
|
568
|
+
|
569
|
+
q.object_group(self){
|
570
|
+
q.breakable
|
571
|
+
members.each{ |mem|
|
572
|
+
q.group{
|
573
|
+
q.text("#{mem}".ljust(15) + "=> ")
|
574
|
+
if mem == 'mode'
|
575
|
+
q.text(sprintf("0%o", send(mem.intern)))
|
576
|
+
else
|
577
|
+
val = self.send(mem.intern)
|
578
|
+
if val.nil?
|
579
|
+
q.text('nil')
|
580
|
+
else
|
581
|
+
q.text(val.to_s)
|
582
|
+
end
|
583
|
+
end
|
584
|
+
}
|
585
|
+
q.comma_breakable unless mem == members.last
|
578
586
|
}
|
579
|
-
|
587
|
+
}
|
588
|
+
end
|
580
589
|
|
581
|
-
|
582
|
-
|
590
|
+
# Since old_init was added strictly to avoid a warning, we remove it now.
|
591
|
+
remove_method(:old_init)
|
583
592
|
|
584
|
-
|
593
|
+
private
|
585
594
|
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
+
# Returns the file system's block size.
|
596
|
+
#
|
597
|
+
def get_blksize(file)
|
598
|
+
size = nil
|
599
|
+
|
600
|
+
sectors = [0].pack('L')
|
601
|
+
bytes = [0].pack('L')
|
602
|
+
free = [0].pack('L')
|
603
|
+
total = [0].pack('L')
|
595
604
|
|
596
|
-
|
597
|
-
|
598
|
-
|
605
|
+
# If there's a drive letter it must contain a trailing backslash.
|
606
|
+
# The dup is necessary here because the function modifies the argument.
|
607
|
+
file = file.dup
|
599
608
|
|
600
|
-
|
601
|
-
|
602
|
-
|
603
|
-
|
604
|
-
|
605
|
-
|
609
|
+
if PathStripToRootA(wide_to_multi(file))
|
610
|
+
file = file[/^[^\0]*/] << ':'
|
611
|
+
file << "\\" unless file[-1].chr == "\\"
|
612
|
+
else
|
613
|
+
file = nil # Default to the root drive on relative paths
|
614
|
+
end
|
606
615
|
|
607
|
-
|
608
|
-
|
609
|
-
|
610
|
-
|
616
|
+
# Don't check for an error here. Just default to nil.
|
617
|
+
if GetDiskFreeSpaceA(file, sectors, bytes, free, total)
|
618
|
+
size = sectors.unpack('L').first * bytes.unpack('L').first
|
619
|
+
end
|
611
620
|
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
|
617
|
-
|
618
|
-
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
|
623
|
-
|
624
|
-
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
|
632
|
-
|
633
|
-
|
634
|
-
|
635
|
-
|
636
|
-
|
637
|
-
|
638
|
-
|
639
|
-
|
640
|
-
|
621
|
+
size
|
622
|
+
end
|
623
|
+
|
624
|
+
# Private method to get a HANDLE when CreateFile() won't cut it.
|
625
|
+
#
|
626
|
+
def get_handle(file)
|
627
|
+
file = file.upcase
|
628
|
+
|
629
|
+
begin
|
630
|
+
hdlTokenHandle = 0.chr * 4
|
631
|
+
|
632
|
+
OpenProcessToken(
|
633
|
+
GetCurrentProcess(),
|
634
|
+
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
|
635
|
+
hdlTokenHandle
|
636
|
+
)
|
637
|
+
|
638
|
+
hdlTokenHandle = hdlTokenHandle.unpack('L').first
|
639
|
+
|
640
|
+
# Get the LUID for shutdown privilege.
|
641
|
+
tmpLuid = 0.chr * 8
|
642
|
+
LookupPrivilegeValue("", "SeDebugPrivilege", tmpLuid)
|
643
|
+
tkp = [1].pack('L') + tmpLuid + [SE_PRIVILEGE_ENABLED].pack('L')
|
644
|
+
|
645
|
+
# Enable the shutdown privilege in the access token of this process.
|
646
|
+
AdjustTokenPrivileges(hdlTokenHandle, 0,tkp, tkp.length , nil, nil)
|
647
|
+
ensure
|
648
|
+
CloseHandle(hdlTokenHandle)
|
649
|
+
end
|
641
650
|
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
|
651
|
-
|
652
|
-
|
653
|
-
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
|
663
|
-
|
664
|
-
|
665
|
-
0,
|
666
|
-
1,
|
667
|
-
2
|
668
|
-
)
|
669
|
-
ensure
|
670
|
-
CloseHandle(process)
|
671
|
-
end
|
672
|
-
|
673
|
-
handle = dup_handle.unpack('L').first
|
674
|
-
buffer = 0.chr * 0x2000
|
675
|
-
NtQueryObject(handle, 1, buffer, 0x2000, nil)
|
676
|
-
len = buffer[0,2].unpack('S').first
|
677
|
-
|
678
|
-
if len>0
|
679
|
-
if buffer[8..-1].upcase[file]
|
680
|
-
return handle
|
681
|
-
end
|
682
|
-
end
|
683
|
-
CloseHandle(handle)
|
684
|
-
end
|
685
|
-
end
|
686
|
-
|
687
|
-
return 0
|
688
|
-
end
|
689
|
-
|
690
|
-
# Returns the file's type (as a numeric).
|
691
|
-
#
|
692
|
-
def get_file_type(file)
|
693
|
-
begin
|
694
|
-
handle = CreateFileW(
|
695
|
-
file,
|
696
|
-
0,
|
651
|
+
# First call is to get the required length
|
652
|
+
handle_info = 0.chr * 4096
|
653
|
+
required = 0.chr * 4
|
654
|
+
NtQuerySystemInformation(16, handle_info, 4096, required)
|
655
|
+
|
656
|
+
# Second call is the actual call
|
657
|
+
handle_info = 0.chr * required.unpack('L').first
|
658
|
+
NtQuerySystemInformation(16, handle_info, handle_info.length, required)
|
659
|
+
|
660
|
+
count = handle_info[0,4].unpack('L').first
|
661
|
+
|
662
|
+
for i in 0...count
|
663
|
+
pid, type, handle, addr, access = handle_info[4+i*16,16].unpack('LSSLL')
|
664
|
+
if access & 0xffff == 3
|
665
|
+
begin
|
666
|
+
process = OpenProcess(0x40,1,pid)
|
667
|
+
dup_handle = 0.chr * 4
|
668
|
+
|
669
|
+
DuplicateHandle(
|
670
|
+
process,
|
671
|
+
handle,
|
672
|
+
GetCurrentProcess(),
|
673
|
+
dup_handle,
|
697
674
|
0,
|
698
|
-
|
699
|
-
|
700
|
-
|
701
|
-
|
702
|
-
|
703
|
-
|
704
|
-
|
705
|
-
|
706
|
-
|
707
|
-
|
708
|
-
|
709
|
-
|
710
|
-
|
711
|
-
|
712
|
-
handle
|
713
|
-
|
714
|
-
|
715
|
-
|
716
|
-
|
717
|
-
|
718
|
-
|
719
|
-
|
675
|
+
1,
|
676
|
+
2
|
677
|
+
)
|
678
|
+
ensure
|
679
|
+
CloseHandle(process)
|
680
|
+
end
|
681
|
+
|
682
|
+
handle = dup_handle.unpack('L').first
|
683
|
+
buffer = 0.chr * 0x2000
|
684
|
+
NtQueryObject(handle, 1, buffer, 0x2000, nil)
|
685
|
+
len = buffer[0,2].unpack('S').first
|
686
|
+
|
687
|
+
if len>0
|
688
|
+
if buffer[8..-1].upcase[file]
|
689
|
+
return handle
|
690
|
+
end
|
691
|
+
end
|
692
|
+
CloseHandle(handle)
|
693
|
+
end
|
694
|
+
end
|
695
|
+
|
696
|
+
return 0
|
697
|
+
end
|
698
|
+
|
699
|
+
# Returns the file's type (as a numeric).
|
700
|
+
#
|
701
|
+
def get_file_type(file)
|
702
|
+
begin
|
703
|
+
handle = CreateFileW(
|
704
|
+
file,
|
705
|
+
0,
|
706
|
+
0,
|
707
|
+
nil,
|
708
|
+
OPEN_EXISTING,
|
709
|
+
FILE_FLAG_BACKUP_SEMANTICS, # Need this for directories
|
710
|
+
nil
|
711
|
+
)
|
720
712
|
|
721
|
-
|
722
|
-
|
723
|
-
|
724
|
-
|
713
|
+
error_num = GetLastError()
|
714
|
+
|
715
|
+
# CreateFile() chokes on locked files
|
716
|
+
if error_num == ERROR_SHARING_VIOLATION
|
717
|
+
drive = file[0,4] + 0.chr * 2
|
718
|
+
device = 0.chr * 512
|
719
|
+
QueryDosDeviceW(drive, device, 256)
|
720
|
+
file = device.strip + 0.chr + file[4..-1]
|
721
|
+
handle = get_handle(file)
|
725
722
|
end
|
726
723
|
|
727
|
-
|
728
|
-
|
724
|
+
# We raise a SystemCallError explicitly here in order to maintain
|
725
|
+
# compatibility with the FileUtils module.
|
726
|
+
if handle == INVALID_HANDLE_VALUE
|
727
|
+
raise SystemCallError, get_last_error(error_num)
|
729
728
|
end
|
730
729
|
|
731
|
-
file_type
|
732
|
-
|
730
|
+
file_type = GetFileType(handle)
|
731
|
+
error_num = GetLastError()
|
732
|
+
ensure
|
733
|
+
CloseHandle(handle)
|
734
|
+
end
|
735
|
+
|
736
|
+
if file_type == FILE_TYPE_UNKNOWN && error_num != NO_ERROR
|
737
|
+
raise SystemCallError, get_last_error(error_num)
|
738
|
+
end
|
739
|
+
|
740
|
+
file_type
|
741
|
+
end
|
733
742
|
|
734
|
-
|
743
|
+
private
|
735
744
|
|
736
|
-
|
737
|
-
|
738
|
-
|
739
|
-
|
740
|
-
end
|
745
|
+
# Verifies that a value is either true or false
|
746
|
+
def check_bool(val)
|
747
|
+
raise TypeError unless val == true || val == false
|
748
|
+
end
|
749
|
+
end
|