sys-filesystem 1.0.0 → 1.1.0
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 +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
|