sys-filesystem 1.1.6 → 1.4.3
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.
- 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
|