sys-filesystem 1.1.6 → 1.4.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- checksums.yaml.gz.sig +0 -0
- data/CHANGES.md +162 -0
- data/Gemfile +2 -0
- data/LICENSE +177 -0
- data/{MANIFEST → MANIFEST.md} +9 -6
- data/README.md +92 -0
- data/Rakefile +14 -24
- data/certs/djberg96_pub.pem +22 -17
- data/examples/example_mount.rb +73 -0
- data/examples/example_stat.rb +2 -0
- data/lib/sys/filesystem.rb +72 -1
- data/lib/sys/unix/sys/filesystem/constants.rb +32 -3
- data/lib/sys/unix/sys/filesystem/functions.rb +36 -9
- data/lib/sys/unix/sys/filesystem/structs.rb +77 -11
- data/lib/sys/unix/sys/filesystem.rb +105 -58
- data/lib/sys/windows/sys/filesystem/functions.rb +11 -8
- data/lib/sys/windows/sys/filesystem/helper.rb +1 -1
- data/lib/sys/windows/sys/filesystem.rb +100 -81
- data/spec/spec_helper.rb +6 -0
- data/spec/sys_filesystem_unix_spec.rb +396 -0
- data/spec/sys_filesystem_windows_spec.rb +348 -0
- data/sys-filesystem.gemspec +15 -7
- data.tar.gz.sig +0 -0
- metadata +75 -61
- metadata.gz.sig +0 -0
- data/CHANGES +0 -89
- data/README +0 -80
- data/test/test_sys_filesystem.rb +0 -7
- data/test/test_sys_filesystem_unix.rb +0 -278
- data/test/test_sys_filesystem_windows.rb +0 -250
@@ -18,6 +18,8 @@ module Sys
|
|
18
18
|
# Error typically raised if any of the Sys::Filesystem methods fail.
|
19
19
|
class Error < StandardError; end
|
20
20
|
|
21
|
+
private_class_method :new
|
22
|
+
|
21
23
|
class Mount
|
22
24
|
# The name of the volume. This is the device mapping.
|
23
25
|
attr_reader :name
|
@@ -65,8 +67,7 @@ module Sys
|
|
65
67
|
# The total number of unused blocks.
|
66
68
|
attr_reader :blocks_free
|
67
69
|
|
68
|
-
# The total number of unused blocks available to unprivileged
|
69
|
-
# processes. Identical to +blocks+ at the moment.
|
70
|
+
# The total number of unused blocks available to unprivileged processes.
|
70
71
|
attr_reader :blocks_available
|
71
72
|
|
72
73
|
# Total number of files/inodes that can be created on the file system.
|
@@ -94,9 +95,12 @@ module Sys
|
|
94
95
|
# The file system type, e.g. NTFS, FAT, etc.
|
95
96
|
attr_reader :base_type
|
96
97
|
|
97
|
-
#
|
98
|
+
# The total amount of free space on the partition.
|
98
99
|
attr_reader :bytes_free
|
99
100
|
|
101
|
+
# The amount of free space available to unprivileged processes.
|
102
|
+
attr_reader :bytes_available
|
103
|
+
|
100
104
|
alias inodes files
|
101
105
|
alias inodes_free files_free
|
102
106
|
alias inodes_available files_available
|
@@ -179,7 +183,7 @@ module Sys
|
|
179
183
|
|
180
184
|
boot_time = get_boot_time
|
181
185
|
|
182
|
-
drives.each
|
186
|
+
drives.each do |drive|
|
183
187
|
mount = Mount.new
|
184
188
|
volume = FFI::MemoryPointer.new(:char, MAXPATH)
|
185
189
|
fsname = FFI::MemoryPointer.new(:char, MAXPATH)
|
@@ -204,7 +208,7 @@ module Sys
|
|
204
208
|
|
205
209
|
# Skip unmounted floppies or cd-roms, or inaccessible drives
|
206
210
|
unless bool
|
207
|
-
if [5,21].include?(FFI.errno) # ERROR_NOT_READY or ERROR_ACCESS_DENIED
|
211
|
+
if [5, 21].include?(FFI.errno) # ERROR_NOT_READY or ERROR_ACCESS_DENIED
|
208
212
|
next
|
209
213
|
else
|
210
214
|
raise SystemCallError.new('GetVolumeInformation', FFI.errno)
|
@@ -216,7 +220,7 @@ module Sys
|
|
216
220
|
|
217
221
|
name = 0.chr * MAXPATH
|
218
222
|
|
219
|
-
if QueryDosDeviceA(drive[0,2], name, name.size) == 0
|
223
|
+
if QueryDosDeviceA(drive[0, 2], name, name.size) == 0
|
220
224
|
raise SystemCallError.new('QueryDosDevice', FFI.errno)
|
221
225
|
end
|
222
226
|
|
@@ -229,7 +233,7 @@ module Sys
|
|
229
233
|
else
|
230
234
|
mounts << mount
|
231
235
|
end
|
232
|
-
|
236
|
+
end
|
233
237
|
|
234
238
|
mounts # Nil if the block form was used.
|
235
239
|
end
|
@@ -242,7 +246,7 @@ module Sys
|
|
242
246
|
# File.mount_point("C:\\Documents and Settings") # => "C:\\'
|
243
247
|
#
|
244
248
|
def self.mount_point(file)
|
245
|
-
wfile = FFI::MemoryPointer.from_string(file.wincode)
|
249
|
+
wfile = FFI::MemoryPointer.from_string(file.to_s.wincode)
|
246
250
|
|
247
251
|
if PathStripToRootW(wfile)
|
248
252
|
wfile.read_string(wfile.size).split("\000\000").first.tr(0.chr, '')
|
@@ -257,16 +261,33 @@ module Sys
|
|
257
261
|
#
|
258
262
|
# Examples:
|
259
263
|
#
|
264
|
+
# # Regular directory
|
260
265
|
# Sys::Filesystem.stat("C:\\")
|
261
266
|
# Sys::Filesystem.stat("C:\\Documents and Settings\\some_user")
|
262
267
|
#
|
268
|
+
# # Pathname
|
269
|
+
# pathname = Pathname.new("C:\\")
|
270
|
+
# Sys::Filesystem.stat(pathname)
|
271
|
+
#
|
272
|
+
# # Dir
|
273
|
+
# dir = Dir.open("C:\\")
|
274
|
+
# Sys::Filesystem.stat(dir)
|
275
|
+
#
|
276
|
+
# Note that on Windows you cannot stat a regular file because
|
277
|
+
# Windows won't like it. It must be a directory in some form.
|
278
|
+
#
|
263
279
|
def self.stat(path)
|
280
|
+
path = path.path if path.respond_to?(:path) # Dir
|
281
|
+
path = path.to_s if path.respond_to?(:to_s) # Pathname
|
282
|
+
|
264
283
|
bytes_avail = FFI::MemoryPointer.new(:ulong_long)
|
265
284
|
bytes_free = FFI::MemoryPointer.new(:ulong_long)
|
266
285
|
total_bytes = FFI::MemoryPointer.new(:ulong_long)
|
267
286
|
|
268
|
-
mpoint = mount_point(path)
|
269
|
-
|
287
|
+
mpoint = mount_point(path).to_s
|
288
|
+
mpoint << '/' unless mpoint.end_with?('/')
|
289
|
+
|
290
|
+
wpath = path.to_s.wincode
|
270
291
|
|
271
292
|
# We need this call for the 64 bit support
|
272
293
|
unless GetDiskFreeSpaceExW(wpath, bytes_avail, total_bytes, bytes_free)
|
@@ -277,24 +298,26 @@ module Sys
|
|
277
298
|
bytes_free = bytes_free.read_ulong_long
|
278
299
|
total_bytes = total_bytes.read_ulong_long
|
279
300
|
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
301
|
+
sectors_ptr = FFI::MemoryPointer.new(:ulong_long)
|
302
|
+
bytes_ptr = FFI::MemoryPointer.new(:ulong_long)
|
303
|
+
free_ptr = FFI::MemoryPointer.new(:ulong_long)
|
304
|
+
total_ptr = FFI::MemoryPointer.new(:ulong_long)
|
284
305
|
|
285
306
|
# We need this call for the total/cluster info, which is not in the Ex call.
|
286
|
-
unless GetDiskFreeSpaceW(wpath,
|
307
|
+
unless GetDiskFreeSpaceW(wpath, sectors_ptr, bytes_ptr, free_ptr, total_ptr)
|
287
308
|
raise SystemCallError.new('GetDiskFreeSpace', FFI.errno)
|
288
309
|
end
|
289
310
|
|
290
|
-
|
291
|
-
|
292
|
-
free = free.read_ulong_long
|
293
|
-
total = total.read_ulong_long
|
311
|
+
sectors_per_cluster = sectors_ptr.read_ulong_long
|
312
|
+
bytes_per_sector = bytes_ptr.read_ulong_long
|
294
313
|
|
295
|
-
|
296
|
-
|
314
|
+
free_ptr.free
|
315
|
+
total_ptr.free
|
316
|
+
|
317
|
+
block_size = sectors_per_cluster * bytes_per_sector
|
318
|
+
blocks_avail = bytes_avail / block_size
|
297
319
|
blocks_free = bytes_free / block_size
|
320
|
+
total_blocks = total_bytes / block_size
|
298
321
|
|
299
322
|
vol_name_ptr = FFI::MemoryPointer.new(:char, MAXPATH)
|
300
323
|
base_type_ptr = FFI::MemoryPointer.new(:char, MAXPATH)
|
@@ -314,7 +337,7 @@ module Sys
|
|
314
337
|
)
|
315
338
|
|
316
339
|
unless bool
|
317
|
-
raise SystemCallError.new('
|
340
|
+
raise SystemCallError.new('GetVolumeInformation', FFI.errno)
|
318
341
|
end
|
319
342
|
|
320
343
|
vol_serial = vol_serial_ptr.read_ulong
|
@@ -322,16 +345,19 @@ module Sys
|
|
322
345
|
flags = flags_ptr.read_ulong
|
323
346
|
base_type = base_type_ptr.read_string(base_type_ptr.size).tr(0.chr, '')
|
324
347
|
|
348
|
+
# Lets explicitly free our pointers
|
325
349
|
vol_name_ptr.free
|
326
350
|
vol_serial_ptr.free
|
327
351
|
name_max_ptr.free
|
328
352
|
flags_ptr.free
|
329
353
|
base_type_ptr.free
|
354
|
+
sectors_ptr.free
|
355
|
+
bytes_ptr.free
|
330
356
|
|
331
357
|
stat_obj = Stat.new
|
332
358
|
stat_obj.instance_variable_set(:@path, path)
|
333
359
|
stat_obj.instance_variable_set(:@block_size, block_size)
|
334
|
-
stat_obj.instance_variable_set(:@blocks,
|
360
|
+
stat_obj.instance_variable_set(:@blocks, total_blocks)
|
335
361
|
stat_obj.instance_variable_set(:@blocks_available, blocks_avail)
|
336
362
|
stat_obj.instance_variable_set(:@blocks_free, blocks_free)
|
337
363
|
stat_obj.instance_variable_set(:@name_max, name_max)
|
@@ -339,11 +365,39 @@ module Sys
|
|
339
365
|
stat_obj.instance_variable_set(:@flags, flags)
|
340
366
|
stat_obj.instance_variable_set(:@filesystem_id, vol_serial)
|
341
367
|
stat_obj.instance_variable_set(:@bytes_free, bytes_free)
|
368
|
+
stat_obj.instance_variable_set(:@bytes_available, bytes_avail)
|
342
369
|
|
343
370
|
stat_obj.freeze # Read-only object
|
344
371
|
end
|
345
372
|
|
346
|
-
|
373
|
+
# Associate a volume with a drive letter or a directory on another volume.
|
374
|
+
#
|
375
|
+
def self.mount(target, source)
|
376
|
+
targetw = target.to_s.wincode
|
377
|
+
sourcew = source.to_s.wincode
|
378
|
+
|
379
|
+
volume_namew = (0.chr * 256).wincode
|
380
|
+
|
381
|
+
unless GetVolumeNameForVolumeMountPointW(sourcew, volume_namew, volume_namew.size)
|
382
|
+
raise SystemCallError.new('GetVolumeNameForVolumeMountPoint', FFI.errno)
|
383
|
+
end
|
384
|
+
|
385
|
+
unless SetVolumeMountPointW(targetw, volume_namew)
|
386
|
+
raise SystemCallError.new('SetVolumeMountPoint', FFI.errno)
|
387
|
+
end
|
388
|
+
|
389
|
+
self
|
390
|
+
end
|
391
|
+
|
392
|
+
# Deletes a drive letter or mounted folder.
|
393
|
+
#
|
394
|
+
def self.umount(mount_point)
|
395
|
+
unless DeleteVolumeMountPoint(mount_point)
|
396
|
+
raise SystemCallError.new('DeleteVolumeMountPoint', FFI.errno)
|
397
|
+
end
|
398
|
+
|
399
|
+
self
|
400
|
+
end
|
347
401
|
|
348
402
|
# This method is used to get the boot time of the system, which is used
|
349
403
|
# for the mount_time attribute within the File.mounts method.
|
@@ -357,72 +411,37 @@ module Sys
|
|
357
411
|
raise Error, e
|
358
412
|
else
|
359
413
|
query = 'select LastBootupTime from Win32_OperatingSystem'
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
return Time.mktime(*time_array)
|
364
|
-
}
|
414
|
+
ole = wmi.ExecQuery(query).ItemIndex(0)
|
415
|
+
time_array = Time.parse(ole.LastBootupTime.split('.').first)
|
416
|
+
Time.mktime(*time_array)
|
365
417
|
end
|
366
418
|
end
|
367
419
|
|
420
|
+
private_class_method :get_boot_time
|
421
|
+
|
368
422
|
# Private method that converts filesystem flags into a comma separated
|
369
423
|
# list of strings. The presentation is meant as a rough analogue to the
|
370
424
|
# way options are presented for Unix filesystems.
|
371
425
|
#
|
372
426
|
def self.get_options(flags)
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
str
|
427
|
+
str = ''
|
428
|
+
str << ' casepres' if CASE_PRESERVED_NAMES & flags > 0
|
429
|
+
str << ' casesens' if CASE_SENSITIVE_SEARCH & flags > 0
|
430
|
+
str << ' compression' if FILE_COMPRESSION & flags > 0
|
431
|
+
str << ' namedstreams' if NAMED_STREAMS & flags > 0
|
432
|
+
str << ' pacls' if PERSISTENT_ACLS & flags > 0
|
433
|
+
str << ' ro' if READ_ONLY_VOLUME & flags > 0
|
434
|
+
str << ' encryption' if SUPPORTS_ENCRYPTION & flags > 0
|
435
|
+
str << ' objids' if SUPPORTS_OBJECT_IDS & flags > 0
|
436
|
+
str << ' rpoints' if SUPPORTS_REPARSE_POINTS & flags > 0
|
437
|
+
str << ' sparse' if SUPPORTS_SPARSE_FILES & flags > 0
|
438
|
+
str << ' unicode' if UNICODE_ON_DISK & flags > 0
|
439
|
+
str << ' compressed' if VOLUME_IS_COMPRESSED & flags > 0
|
440
|
+
|
441
|
+
str.tr!(' ', ',')
|
442
|
+
str[1..-1] # Ignore the first comma
|
390
443
|
end
|
391
|
-
end
|
392
|
-
end
|
393
|
-
|
394
|
-
# Some convenient methods for converting bytes to kb, mb, and gb.
|
395
|
-
#
|
396
|
-
class Numeric
|
397
|
-
# call-seq:
|
398
|
-
# <tt>num</tt>.to_kb
|
399
|
-
#
|
400
|
-
# Returns +num+ in terms of kilobytes.
|
401
|
-
def to_kb
|
402
|
-
self / 1024
|
403
|
-
end
|
404
|
-
|
405
|
-
# call-seq:
|
406
|
-
# <tt>num</tt>.to_mb
|
407
|
-
#
|
408
|
-
# Returns +num+ in terms of megabytes.
|
409
|
-
def to_mb
|
410
|
-
self / 1048576
|
411
|
-
end
|
412
|
-
|
413
|
-
# call-seq:
|
414
|
-
# <tt>num</tt>.to_gb
|
415
|
-
#
|
416
|
-
# Returns +num+ in terms of gigabytes.
|
417
|
-
def to_gb
|
418
|
-
self / 1073741824
|
419
|
-
end
|
420
444
|
|
421
|
-
|
422
|
-
# <tt>num</tt>.to_gb
|
423
|
-
#
|
424
|
-
# Returns +num+ in terms of terabytes.
|
425
|
-
def to_tb
|
426
|
-
self / 1099511627776
|
445
|
+
private_class_method :get_options
|
427
446
|
end
|
428
447
|
end
|