sys-filesystem 1.0.0 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES +6 -1
- data/README +5 -14
- data/Rakefile +0 -10
- data/lib/unix/sys/filesystem.rb +1 -1
- data/lib/windows/sys/filesystem.rb +368 -382
- data/lib/windows/sys/filesystem/constants.rb +22 -0
- data/lib/windows/sys/filesystem/functions.rb +36 -0
- data/lib/windows/sys/filesystem/helper.rb +7 -0
- data/lib/windows/sys/filesystem/structs.rb +0 -0
- data/sys-filesystem.gemspec +4 -4
- data/test/test_sys_filesystem_unix.rb +2 -6
- data/test/test_sys_filesystem_windows.rb +207 -183
- metadata +56 -67
data/CHANGES
CHANGED
@@ -1,4 +1,9 @@
|
|
1
|
-
== 1.
|
1
|
+
== 1.1.0 - 19-Jan-2013
|
2
|
+
* Converted the Windows source code to use FFI. Consequently, there is now
|
3
|
+
a single gem rather than separate gems for Windows and Unix.
|
4
|
+
* Revamped the Windows tests.
|
5
|
+
|
6
|
+
== 1.0.0 - 11-Jan-2012
|
2
7
|
* Converted everything from C to FFI for the Unix flavors. The Windows
|
3
8
|
source code remains untouched.
|
4
9
|
|
data/README
CHANGED
@@ -4,11 +4,6 @@
|
|
4
4
|
= Installation
|
5
5
|
gem install sys-filesystem
|
6
6
|
|
7
|
-
== Windows
|
8
|
-
If the installation command above doesn't work try this:
|
9
|
-
|
10
|
-
gem install sys-filesystem --platform x86-mingw32
|
11
|
-
|
12
7
|
= Synopsis
|
13
8
|
require 'sys/filesystem'
|
14
9
|
include Sys
|
@@ -41,12 +36,8 @@
|
|
41
36
|
puts Filesystem.mount_point('/home/djberge/some_file.txt') => '/home'
|
42
37
|
|
43
38
|
= Notes
|
44
|
-
|
45
|
-
|
46
|
-
turn wraps native Windows functions.
|
47
|
-
|
48
|
-
=== UNIX
|
49
|
-
This is a C extension that wraps statvfs, etc.
|
39
|
+
This is a pure Ruby implementation that uses FFI. This means it should work
|
40
|
+
with JRuby, too.
|
50
41
|
|
51
42
|
= Sample code
|
52
43
|
Run 'rake example' if you want to see a basic sample run. The actual code
|
@@ -54,7 +45,7 @@
|
|
54
45
|
|
55
46
|
= Known Bugs
|
56
47
|
None that I'm aware of. Please report bugs on the project page at
|
57
|
-
|
48
|
+
https://github.com/djberg96/sys-filesystem
|
58
49
|
|
59
50
|
= Future Plans
|
60
51
|
Suggestions welcome.
|
@@ -65,13 +56,13 @@
|
|
65
56
|
|
66
57
|
Park Heesob, for implementation and API ideas for the MS Windows version.
|
67
58
|
|
68
|
-
Nobuyoshi Miyokawa, for adding FreeBSD and OS X support.
|
59
|
+
Nobuyoshi Miyokawa, for adding the original FreeBSD and OS X support.
|
69
60
|
|
70
61
|
= License
|
71
62
|
Artistic 2.0
|
72
63
|
|
73
64
|
= Copyright
|
74
|
-
(C) 2003-
|
65
|
+
(C) 2003-2013 Daniel J. Berger
|
75
66
|
All Rights Reserved
|
76
67
|
|
77
68
|
= Warranty
|
data/Rakefile
CHANGED
@@ -26,16 +26,6 @@ namespace :gem do
|
|
26
26
|
desc "Build the sys-filesystem gem"
|
27
27
|
task :create do |t|
|
28
28
|
spec = eval(IO.read('sys-filesystem.gemspec'))
|
29
|
-
|
30
|
-
if File::ALT_SEPARATOR
|
31
|
-
spec.add_dependency('windows-pr', '>= 1.0.5')
|
32
|
-
spec.platform = Gem::Platform::CURRENT
|
33
|
-
spec.platform.cpu = 'universal'
|
34
|
-
spec.platform.version = nil
|
35
|
-
else
|
36
|
-
spec.add_dependency('ffi', '>= 1.0.0')
|
37
|
-
end
|
38
|
-
|
39
29
|
Gem::Builder.new(spec).build
|
40
30
|
end
|
41
31
|
|
data/lib/unix/sys/filesystem.rb
CHANGED
@@ -1,9 +1,7 @@
|
|
1
|
-
require '
|
2
|
-
require '
|
3
|
-
require '
|
4
|
-
|
5
|
-
require 'windows/handle'
|
6
|
-
require 'windows/limits'
|
1
|
+
require File.join(File.dirname(__FILE__), 'filesystem', 'constants')
|
2
|
+
require File.join(File.dirname(__FILE__), 'filesystem', 'functions')
|
3
|
+
require File.join(File.dirname(__FILE__), 'filesystem', 'helper')
|
4
|
+
|
7
5
|
require 'socket'
|
8
6
|
require 'win32ole'
|
9
7
|
require 'date'
|
@@ -12,397 +10,385 @@ require 'time'
|
|
12
10
|
# The Sys module serves as a namespace only.
|
13
11
|
module Sys
|
14
12
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
include Windows::Limits
|
20
|
-
|
21
|
-
extend Windows::Error
|
22
|
-
extend Windows::FileSystem
|
23
|
-
extend Windows::Volume
|
24
|
-
extend Windows::Path
|
25
|
-
|
26
|
-
# Error typically raised if any of the Sys::Filesystem methods fail.
|
27
|
-
class Error < StandardError; end
|
28
|
-
|
29
|
-
CASE_SENSITIVE_SEARCH = 0x00000001
|
30
|
-
CASE_PRESERVED_NAMES = 0x00000002
|
31
|
-
UNICODE_ON_DISK = 0x00000004
|
32
|
-
PERSISTENT_ACLS = 0x00000008
|
33
|
-
FILE_COMPRESSION = 0x00000010
|
34
|
-
VOLUME_QUOTAS = 0x00000020
|
35
|
-
SUPPORTS_SPARSE_FILES = 0x00000040
|
36
|
-
SUPPORTS_REPARSE_POINTS = 0x00000080
|
37
|
-
SUPPORTS_REMOTE_STORAGE = 0x00000100
|
38
|
-
VOLUME_IS_COMPRESSED = 0x00008000
|
39
|
-
SUPPORTS_OBJECT_IDS = 0x00010000
|
40
|
-
SUPPORTS_ENCRYPTION = 0x00020000
|
41
|
-
NAMED_STREAMS = 0x00040000
|
42
|
-
READ_ONLY_VOLUME = 0x00080000
|
43
|
-
|
44
|
-
# The version of the sys-filesystem library.
|
45
|
-
VERSION = '1.0.0'
|
46
|
-
|
47
|
-
class Mount
|
48
|
-
# The name of the volume. This is the device mapping.
|
49
|
-
attr_reader :name
|
50
|
-
|
51
|
-
# The last time the volume was mounted. For MS Windows this equates
|
52
|
-
# to your system's boot time.
|
53
|
-
attr_reader :mount_time
|
54
|
-
|
55
|
-
# The type of mount, e.g. NTFS, UDF, etc.
|
56
|
-
attr_reader :mount_type
|
57
|
-
|
58
|
-
# The volume mount point, e.g. 'C:\'
|
59
|
-
attr_reader :mount_point
|
60
|
-
|
61
|
-
# Various comma separated options that reflect the volume's features
|
62
|
-
attr_reader :options
|
63
|
-
|
64
|
-
# Always nil on MS Windows. Provided for interface compatibility only.
|
65
|
-
attr_reader :pass_number
|
66
|
-
|
67
|
-
# Always nil on MS Windows. Provided for interface compatibility only.
|
68
|
-
attr_reader :frequency
|
69
|
-
|
70
|
-
alias fsname name
|
71
|
-
alias dir mount_point
|
72
|
-
alias opts options
|
73
|
-
alias passno pass_number
|
74
|
-
alias freq frequency
|
75
|
-
end
|
76
|
-
|
77
|
-
class Stat
|
78
|
-
# The path of the file system.
|
79
|
-
attr_reader :path
|
80
|
-
|
81
|
-
# The file system block size. MS Windows typically defaults to 4096.
|
82
|
-
attr_reader :block_size
|
83
|
-
|
84
|
-
# Fragment size. Meaningless at the moment.
|
85
|
-
attr_reader :fragment_size
|
86
|
-
|
87
|
-
# The total number of blocks available (used or unused) on the file
|
88
|
-
# system.
|
89
|
-
attr_reader :blocks
|
90
|
-
|
91
|
-
# The total number of unused blocks.
|
92
|
-
attr_reader :blocks_free
|
93
|
-
|
94
|
-
# The total number of unused blocks available to unprivileged
|
95
|
-
# processes. Identical to +blocks+ at the moment.
|
96
|
-
attr_reader :blocks_available
|
97
|
-
|
98
|
-
# Total number of files/inodes that can be created on the file system.
|
99
|
-
# This attribute is always nil on MS Windows.
|
100
|
-
attr_reader :files
|
13
|
+
# The Filesystem class encapsulates information about your filesystem.
|
14
|
+
class Filesystem
|
15
|
+
include Sys::Filesystem::Constants
|
16
|
+
extend Sys::Filesystem::Functions
|
101
17
|
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
18
|
+
# Error typically raised if any of the Sys::Filesystem methods fail.
|
19
|
+
class Error < StandardError; end
|
20
|
+
|
21
|
+
# The version of the sys-filesystem library.
|
22
|
+
VERSION = '1.1.0'
|
23
|
+
|
24
|
+
class Mount
|
25
|
+
# The name of the volume. This is the device mapping.
|
26
|
+
attr_reader :name
|
27
|
+
|
28
|
+
# The last time the volume was mounted. For MS Windows this equates
|
29
|
+
# to your system's boot time.
|
30
|
+
attr_reader :mount_time
|
31
|
+
|
32
|
+
# The type of mount, e.g. NTFS, UDF, etc.
|
33
|
+
attr_reader :mount_type
|
34
|
+
|
35
|
+
# The volume mount point, e.g. 'C:\'
|
36
|
+
attr_reader :mount_point
|
37
|
+
|
38
|
+
# Various comma separated options that reflect the volume's features
|
39
|
+
attr_reader :options
|
40
|
+
|
41
|
+
# Always nil on MS Windows. Provided for interface compatibility only.
|
42
|
+
attr_reader :pass_number
|
43
|
+
|
44
|
+
# Always nil on MS Windows. Provided for interface compatibility only.
|
45
|
+
attr_reader :frequency
|
46
|
+
|
47
|
+
alias fsname name
|
48
|
+
alias dir mount_point
|
49
|
+
alias opts options
|
50
|
+
alias passno pass_number
|
51
|
+
alias freq frequency
|
52
|
+
end
|
53
|
+
|
54
|
+
class Stat
|
55
|
+
# The path of the file system.
|
56
|
+
attr_reader :path
|
57
|
+
|
58
|
+
# The file system block size. MS Windows typically defaults to 4096.
|
59
|
+
attr_reader :block_size
|
60
|
+
|
61
|
+
# Fragment size. Meaningless at the moment.
|
62
|
+
attr_reader :fragment_size
|
63
|
+
|
64
|
+
# The total number of blocks available (used or unused) on the file
|
65
|
+
# system.
|
66
|
+
attr_reader :blocks
|
67
|
+
|
68
|
+
# The total number of unused blocks.
|
69
|
+
attr_reader :blocks_free
|
70
|
+
|
71
|
+
# The total number of unused blocks available to unprivileged
|
72
|
+
# processes. Identical to +blocks+ at the moment.
|
73
|
+
attr_reader :blocks_available
|
74
|
+
|
75
|
+
# Total number of files/inodes that can be created on the file system.
|
76
|
+
# This attribute is always nil on MS Windows.
|
77
|
+
attr_reader :files
|
78
|
+
|
79
|
+
# Total number of free files/inodes that can be created on the file
|
80
|
+
# system. This attribute is always nil on MS Windows.
|
81
|
+
attr_reader :files_free
|
82
|
+
|
83
|
+
# Total number of available files/inodes for unprivileged processes
|
84
|
+
# that can be created on the file system. This attribute is always
|
85
|
+
# nil on MS Windows.
|
86
|
+
attr_reader :files_available
|
87
|
+
|
88
|
+
# The file system volume id.
|
89
|
+
attr_reader :filesystem_id
|
90
|
+
|
91
|
+
# A bit mask of file system flags.
|
92
|
+
attr_reader :flags
|
93
|
+
|
94
|
+
# The maximum length of a file name permitted on the file system.
|
95
|
+
attr_reader :name_max
|
96
|
+
|
97
|
+
# The file system type, e.g. NTFS, FAT, etc.
|
98
|
+
attr_reader :base_type
|
99
|
+
|
100
|
+
alias inodes files
|
101
|
+
alias inodes_free files_free
|
102
|
+
alias inodes_available files_available
|
103
|
+
end
|
104
|
+
|
105
|
+
# Yields a Filesystem::Mount object for each volume on your system in
|
106
|
+
# block form. Returns an array of Filesystem::Mount objects in non-block
|
107
|
+
# form.
|
108
|
+
#
|
109
|
+
# Example:
|
110
|
+
#
|
111
|
+
# Sys::Filesystem.mounts{ |mount|
|
112
|
+
# p mt.name # => \\Device\\HarddiskVolume1
|
113
|
+
# p mt.mount_point # => C:\
|
114
|
+
# p mt.mount_time # => Thu Dec 18 20:12:08 -0700 2008
|
115
|
+
# p mt.mount_type # => NTFS
|
116
|
+
# p mt.options # => casepres,casesens,ro,unicode
|
117
|
+
# p mt.pass_number # => nil
|
118
|
+
# p mt.dump_freq # => nil
|
119
|
+
# }
|
120
|
+
#
|
121
|
+
# This method is a bit of a fudge for MS Windows in the name of interface
|
122
|
+
# compatibility because this method deals with volumes, not actual mount
|
123
|
+
# points. But, I believe it provides the sort of information many users
|
124
|
+
# want at a glance.
|
125
|
+
#
|
126
|
+
# The possible values for the +options+ and their meanings are as follows:
|
127
|
+
#
|
128
|
+
# casepres => The filesystem preserves the case of file names when it places a name on disk.
|
129
|
+
# casesens => The filesystem supports case-sensitive file names.
|
130
|
+
# compression => The filesystem supports file-based compression.
|
131
|
+
# namedstreams => The filesystem supports named streams.
|
132
|
+
# pacls => The filesystem preserves and enforces access control lists.
|
133
|
+
# ro => The filesystem is read-only.
|
134
|
+
# encryption => The filesystem supports the Encrypted File System (EFS).
|
135
|
+
# objids => The filesystem supports object identifiers.
|
136
|
+
# rpoints => The filesystem supports reparse points.
|
137
|
+
# sparse => The filesystem supports sparse files.
|
138
|
+
# unicode => The filesystem supports Unicode in file names as they appear on disk.
|
139
|
+
# compressed => The filesystem is compressed.
|
140
|
+
#
|
141
|
+
#--
|
142
|
+
# I couldn't really find a good reason to use the wide functions for this
|
143
|
+
# method. If you have one, patches welcome.
|
144
|
+
#
|
145
|
+
def self.mounts
|
146
|
+
# First call, get needed buffer size
|
147
|
+
buffer = 0.chr
|
148
|
+
length = GetLogicalDriveStringsA(buffer.size, buffer)
|
149
|
+
|
150
|
+
if length == 0
|
151
|
+
raise SystemCallError.new('GetLogicalDriveStrings', FFI.errno)
|
152
|
+
else
|
153
|
+
buffer = 0.chr * length
|
154
|
+
end
|
119
155
|
|
120
|
-
|
121
|
-
attr_reader :base_type
|
156
|
+
mounts = block_given? ? nil : []
|
122
157
|
|
123
|
-
|
124
|
-
|
125
|
-
|
158
|
+
# Try again with new buffer size
|
159
|
+
if GetLogicalDriveStringsA(buffer.size, buffer) == 0
|
160
|
+
raise SystemCallError.new('GetLogicalDriveStrings', FFI.errno)
|
126
161
|
end
|
127
162
|
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
volume.size,
|
202
|
-
volume_serial_number,
|
203
|
-
max_component_length,
|
204
|
-
filesystem_flags,
|
205
|
-
fsname,
|
206
|
-
fsname.size
|
207
|
-
)
|
208
|
-
|
209
|
-
# Skip unmounted floppies or cd-roms
|
210
|
-
unless bool
|
211
|
-
errnum = GetLastError()
|
212
|
-
if errnum == ERROR_NOT_READY
|
213
|
-
next
|
214
|
-
else
|
215
|
-
raise Error, get_last_error(errnum)
|
216
|
-
end
|
217
|
-
end
|
218
|
-
|
219
|
-
filesystem_flags = filesystem_flags.unpack('L')[0]
|
220
|
-
|
221
|
-
name = 0.chr * MAXPATH
|
222
|
-
|
223
|
-
if QueryDosDevice(drive[0,2], name, name.size) == 0
|
224
|
-
raise Error, get_last_error
|
225
|
-
end
|
226
|
-
|
227
|
-
mount.instance_variable_set(:@name, name.strip)
|
228
|
-
mount.instance_variable_set(:@mount_type, fsname.strip)
|
229
|
-
mount.instance_variable_set(:@options, get_options(filesystem_flags))
|
230
|
-
|
231
|
-
if block_given?
|
232
|
-
yield mount
|
233
|
-
else
|
234
|
-
mounts << mount
|
235
|
-
end
|
236
|
-
}
|
237
|
-
|
238
|
-
mounts # Nil if the block form was used.
|
163
|
+
drives = buffer.split(0.chr)
|
164
|
+
|
165
|
+
boot_time = get_boot_time
|
166
|
+
|
167
|
+
drives.each{ |drive|
|
168
|
+
mount = Mount.new
|
169
|
+
volume = FFI::MemoryPointer.new(:char, MAXPATH)
|
170
|
+
fsname = FFI::MemoryPointer.new(:char, MAXPATH)
|
171
|
+
|
172
|
+
mount.instance_variable_set(:@mount_point, drive)
|
173
|
+
mount.instance_variable_set(:@mount_time, boot_time)
|
174
|
+
|
175
|
+
volume_serial_number = FFI::MemoryPointer.new(:ulong)
|
176
|
+
max_component_length = FFI::MemoryPointer.new(:ulong)
|
177
|
+
filesystem_flags = FFI::MemoryPointer.new(:ulong)
|
178
|
+
|
179
|
+
bool = GetVolumeInformationA(
|
180
|
+
drive,
|
181
|
+
volume,
|
182
|
+
volume.size,
|
183
|
+
volume_serial_number,
|
184
|
+
max_component_length,
|
185
|
+
filesystem_flags,
|
186
|
+
fsname,
|
187
|
+
fsname.size
|
188
|
+
)
|
189
|
+
|
190
|
+
# Skip unmounted floppies or cd-roms, or inaccessible drives
|
191
|
+
unless bool
|
192
|
+
if [5,21].include?(FFI.errno) # ERROR_NOT_READY or ERROR_ACCESS_DENIED
|
193
|
+
next
|
194
|
+
else
|
195
|
+
raise SystemCallError.new('GetVolumeInformation', FFI.errno)
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
filesystem_flags = filesystem_flags.read_ulong
|
200
|
+
fsname = fsname.read_string
|
201
|
+
|
202
|
+
name = 0.chr * MAXPATH
|
203
|
+
|
204
|
+
if QueryDosDeviceA(drive[0,2], name, name.size) == 0
|
205
|
+
raise SystemCallError.new('QueryDosDevice', FFI.errno)
|
206
|
+
end
|
207
|
+
|
208
|
+
mount.instance_variable_set(:@name, name.strip)
|
209
|
+
mount.instance_variable_set(:@mount_type, fsname)
|
210
|
+
mount.instance_variable_set(:@options, get_options(filesystem_flags))
|
211
|
+
|
212
|
+
if block_given?
|
213
|
+
yield mount
|
214
|
+
else
|
215
|
+
mounts << mount
|
216
|
+
end
|
217
|
+
}
|
218
|
+
|
219
|
+
mounts # Nil if the block form was used.
|
220
|
+
end
|
221
|
+
|
222
|
+
# Returns the mount point for the given +file+. For MS Windows this
|
223
|
+
# means the root of the path.
|
224
|
+
#
|
225
|
+
# Example:
|
226
|
+
#
|
227
|
+
# File.mount_point("C:\\Documents and Settings") # => "C:\\'
|
228
|
+
#
|
229
|
+
def self.mount_point(file)
|
230
|
+
wfile = FFI::MemoryPointer.from_string(file.wincode)
|
231
|
+
|
232
|
+
if PathStripToRootW(wfile)
|
233
|
+
wfile.read_string(wfile.size).split("\000\000").first.tr(0.chr, '')
|
234
|
+
else
|
235
|
+
nil
|
239
236
|
end
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
237
|
+
end
|
238
|
+
|
239
|
+
# Returns a Filesystem::Stat object that contains information about the
|
240
|
+
# +path+ file system.
|
241
|
+
#
|
242
|
+
# Examples:
|
243
|
+
#
|
244
|
+
# File.stat("C:\\")
|
245
|
+
# File.stat("C:\\Documents and Settings\\some_user")
|
246
|
+
#
|
247
|
+
def self.stat(path)
|
248
|
+
bytes_avail = FFI::MemoryPointer.new(:ulong_long)
|
249
|
+
bytes_free = FFI::MemoryPointer.new(:ulong_long)
|
250
|
+
total_bytes = FFI::MemoryPointer.new(:ulong_long)
|
251
|
+
|
252
|
+
wpath = path.wincode
|
253
|
+
|
254
|
+
unless GetDiskFreeSpaceExW(wpath, bytes_avail, total_bytes, bytes_free)
|
255
|
+
raise SystemCallError.new('GetDiskFreeSpaceEx', FFI.errno)
|
252
256
|
end
|
253
257
|
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
# Examples:
|
258
|
-
#
|
259
|
-
# File.stat("C:\\")
|
260
|
-
# File.stat("C:\\Documents and Settings\\some_user")
|
261
|
-
#
|
262
|
-
def self.stat(path)
|
263
|
-
bytes_avail = [0].pack('Q')
|
264
|
-
bytes_free = [0].pack('Q')
|
265
|
-
total_bytes = [0].pack('Q')
|
266
|
-
|
267
|
-
unless GetDiskFreeSpaceEx(path, bytes_avail, total_bytes, bytes_free)
|
268
|
-
raise Error, get_last_error
|
269
|
-
end
|
270
|
-
|
271
|
-
bytes_avail = bytes_avail.unpack('Q').first
|
272
|
-
bytes_free = bytes_free.unpack('Q').first
|
273
|
-
total_bytes = total_bytes.unpack('Q').first
|
274
|
-
|
275
|
-
sectors = [0].pack('Q')
|
276
|
-
bytes = [0].pack('Q')
|
277
|
-
free = [0].pack('Q')
|
278
|
-
total = [0].pack('Q')
|
279
|
-
|
280
|
-
unless GetDiskFreeSpace(path, sectors, bytes, free, total)
|
281
|
-
raise Error, get_last_error
|
282
|
-
end
|
283
|
-
|
284
|
-
sectors = sectors.unpack('Q').first
|
285
|
-
bytes = bytes.unpack('Q').first
|
286
|
-
free = free.unpack('Q').first
|
287
|
-
total = total.unpack('Q').first
|
288
|
-
|
289
|
-
block_size = sectors * bytes
|
290
|
-
blocks_avail = total_bytes / block_size
|
291
|
-
blocks_free = bytes_free / block_size
|
292
|
-
|
293
|
-
vol_name = 0.chr * 260
|
294
|
-
base_type = 0.chr * 260
|
295
|
-
vol_serial = [0].pack('L')
|
296
|
-
name_max = [0].pack('L')
|
297
|
-
flags = [0].pack('L')
|
298
|
-
|
299
|
-
bool = GetVolumeInformation(
|
300
|
-
path,
|
301
|
-
vol_name,
|
302
|
-
vol_name.size,
|
303
|
-
vol_serial,
|
304
|
-
name_max,
|
305
|
-
flags,
|
306
|
-
base_type,
|
307
|
-
base_type.size
|
308
|
-
)
|
309
|
-
|
310
|
-
unless bool
|
311
|
-
raise Error, get_last_error
|
312
|
-
end
|
313
|
-
|
314
|
-
vol_serial = vol_serial.unpack('L').first
|
315
|
-
name_max = name_max.unpack('L').first
|
316
|
-
flags = flags.unpack('L').first
|
317
|
-
base_type = base_type[/^[^\0]*/]
|
318
|
-
|
319
|
-
stat_obj = Stat.new
|
320
|
-
stat_obj.instance_variable_set(:@path, path)
|
321
|
-
stat_obj.instance_variable_set(:@block_size, block_size)
|
322
|
-
stat_obj.instance_variable_set(:@blocks, blocks_avail)
|
323
|
-
stat_obj.instance_variable_set(:@blocks_available, blocks_avail)
|
324
|
-
stat_obj.instance_variable_set(:@blocks_free, blocks_free)
|
325
|
-
stat_obj.instance_variable_set(:@name_max, name_max)
|
326
|
-
stat_obj.instance_variable_set(:@base_type, base_type)
|
327
|
-
stat_obj.instance_variable_set(:@flags, flags)
|
328
|
-
stat_obj.instance_variable_set(:@filesystem_id, vol_serial)
|
329
|
-
|
330
|
-
stat_obj.freeze # Read-only object
|
331
|
-
end
|
258
|
+
bytes_avail = bytes_avail.read_ulong_long
|
259
|
+
bytes_free = bytes_free.read_ulong_long
|
260
|
+
total_bytes = total_bytes.read_ulong_long
|
332
261
|
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
cs = "winmgmts://#{host}/root/cimv2"
|
341
|
-
begin
|
342
|
-
wmi = WIN32OLE.connect(cs)
|
343
|
-
rescue WIN32OLERuntimeError => e
|
344
|
-
raise Error, e
|
345
|
-
else
|
346
|
-
query = 'select LastBootupTime from Win32_OperatingSystem'
|
347
|
-
results = wmi.ExecQuery(query)
|
348
|
-
results.each{ |ole|
|
349
|
-
time_array = Time.parse(ole.LastBootupTime.split('.').first)
|
350
|
-
return Time.mktime(*time_array)
|
351
|
-
}
|
352
|
-
end
|
262
|
+
sectors = FFI::MemoryPointer.new(:ulong_long)
|
263
|
+
bytes = FFI::MemoryPointer.new(:ulong_long)
|
264
|
+
free = FFI::MemoryPointer.new(:ulong_long)
|
265
|
+
total = FFI::MemoryPointer.new(:ulong_long)
|
266
|
+
|
267
|
+
unless GetDiskFreeSpaceW(wpath, sectors, bytes, free, total)
|
268
|
+
raise SystemCallError.new('GetDiskFreeSpace', FFI.errno)
|
353
269
|
end
|
354
270
|
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
271
|
+
sectors = sectors.read_ulong_long
|
272
|
+
bytes = bytes.read_ulong_long
|
273
|
+
free = free.read_ulong_long
|
274
|
+
total = total.read_ulong_long
|
275
|
+
|
276
|
+
block_size = sectors * bytes
|
277
|
+
blocks_avail = total_bytes / block_size
|
278
|
+
blocks_free = bytes_free / block_size
|
279
|
+
|
280
|
+
vol_name = FFI::MemoryPointer.new(:char, MAXPATH)
|
281
|
+
base_type = FFI::MemoryPointer.new(:char, MAXPATH)
|
282
|
+
vol_serial = FFI::MemoryPointer.new(:ulong)
|
283
|
+
name_max = FFI::MemoryPointer.new(:ulong)
|
284
|
+
flags = FFI::MemoryPointer.new(:ulong)
|
285
|
+
|
286
|
+
bool = GetVolumeInformationW(
|
287
|
+
wpath,
|
288
|
+
vol_name,
|
289
|
+
vol_name.size,
|
290
|
+
vol_serial,
|
291
|
+
name_max,
|
292
|
+
flags,
|
293
|
+
base_type,
|
294
|
+
base_type.size
|
295
|
+
)
|
296
|
+
|
297
|
+
unless bool
|
298
|
+
raise SystemCallError.new('GetVolumInformation', FFI.errno)
|
377
299
|
end
|
378
300
|
|
379
|
-
|
301
|
+
vol_serial = vol_serial.read_ulong
|
302
|
+
name_max = name_max.read_ulong
|
303
|
+
flags = flags.read_ulong
|
304
|
+
base_type = base_type.read_string(base_type.size).tr(0.chr, '')
|
305
|
+
|
306
|
+
stat_obj = Stat.new
|
307
|
+
stat_obj.instance_variable_set(:@path, path)
|
308
|
+
stat_obj.instance_variable_set(:@block_size, block_size)
|
309
|
+
stat_obj.instance_variable_set(:@blocks, blocks_avail)
|
310
|
+
stat_obj.instance_variable_set(:@blocks_available, blocks_avail)
|
311
|
+
stat_obj.instance_variable_set(:@blocks_free, blocks_free)
|
312
|
+
stat_obj.instance_variable_set(:@name_max, name_max)
|
313
|
+
stat_obj.instance_variable_set(:@base_type, base_type)
|
314
|
+
stat_obj.instance_variable_set(:@flags, flags)
|
315
|
+
stat_obj.instance_variable_set(:@filesystem_id, vol_serial)
|
316
|
+
|
317
|
+
stat_obj.freeze # Read-only object
|
318
|
+
end
|
319
|
+
|
320
|
+
private
|
321
|
+
|
322
|
+
# This method is used to get the boot time of the system, which is used
|
323
|
+
# for the mount_time attribute within the File.mounts method.
|
324
|
+
#
|
325
|
+
def self.get_boot_time
|
326
|
+
host = Socket.gethostname
|
327
|
+
cs = "winmgmts://#{host}/root/cimv2"
|
328
|
+
begin
|
329
|
+
wmi = WIN32OLE.connect(cs)
|
330
|
+
rescue WIN32OLERuntimeError => e
|
331
|
+
raise Error, e
|
332
|
+
else
|
333
|
+
query = 'select LastBootupTime from Win32_OperatingSystem'
|
334
|
+
results = wmi.ExecQuery(query)
|
335
|
+
results.each{ |ole|
|
336
|
+
time_array = Time.parse(ole.LastBootupTime.split('.').first)
|
337
|
+
return Time.mktime(*time_array)
|
338
|
+
}
|
339
|
+
end
|
340
|
+
end
|
341
|
+
|
342
|
+
# Private method that converts filesystem flags into a comma separated
|
343
|
+
# list of strings. The presentation is meant as a rough analogue to the
|
344
|
+
# way options are presented for Unix filesystems.
|
345
|
+
#
|
346
|
+
def self.get_options(flags)
|
347
|
+
str = ""
|
348
|
+
str << " casepres" if CASE_PRESERVED_NAMES & flags > 0
|
349
|
+
str << " casesens" if CASE_SENSITIVE_SEARCH & flags > 0
|
350
|
+
str << " compression" if FILE_COMPRESSION & flags > 0
|
351
|
+
str << " namedstreams" if NAMED_STREAMS & flags > 0
|
352
|
+
str << " pacls" if PERSISTENT_ACLS & flags > 0
|
353
|
+
str << " ro" if READ_ONLY_VOLUME & flags > 0
|
354
|
+
str << " encryption" if SUPPORTS_ENCRYPTION & flags > 0
|
355
|
+
str << " objids" if SUPPORTS_OBJECT_IDS & flags > 0
|
356
|
+
str << " rpoints" if SUPPORTS_REPARSE_POINTS & flags > 0
|
357
|
+
str << " sparse" if SUPPORTS_SPARSE_FILES & flags > 0
|
358
|
+
str << " unicode" if UNICODE_ON_DISK & flags > 0
|
359
|
+
str << " compressed" if VOLUME_IS_COMPRESSED & flags > 0
|
360
|
+
|
361
|
+
str.tr!(' ', ',')
|
362
|
+
str = str[1..-1] # Ignore the first comma
|
363
|
+
str
|
364
|
+
end
|
365
|
+
end
|
380
366
|
end
|
381
367
|
|
382
368
|
# Some convenient methods for converting bytes to kb, mb, and gb.
|
383
|
-
#
|
369
|
+
#
|
384
370
|
class Fixnum
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
371
|
+
# call-seq:
|
372
|
+
# <tt>fix</tt>.to_kb
|
373
|
+
#
|
374
|
+
# Returns +fix+ in terms of kilobytes.
|
375
|
+
def to_kb
|
376
|
+
self / 1024
|
377
|
+
end
|
378
|
+
|
379
|
+
# call-seq:
|
380
|
+
# <tt>fix</tt>.to_mb
|
381
|
+
#
|
382
|
+
# Returns +fix+ in terms of megabytes.
|
383
|
+
def to_mb
|
384
|
+
self / 1048576
|
385
|
+
end
|
386
|
+
|
387
|
+
# call-seq:
|
388
|
+
# <tt>fix</tt>.to_gb
|
389
|
+
#
|
390
|
+
# Returns +fix+ in terms of gigabytes.
|
391
|
+
def to_gb
|
392
|
+
self / 1073741824
|
393
|
+
end
|
408
394
|
end
|