win32-file-stat 1.3.4 → 1.3.5
Sign up to get free protection for your applications and to get access to all the features.
- 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
|