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.
@@ -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
- # Returns the total amount of free space on the partition.
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{ |drive|
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
- wpath = path.wincode
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
- sectors = FFI::MemoryPointer.new(:ulong_long)
281
- bytes = FFI::MemoryPointer.new(:ulong_long)
282
- free = FFI::MemoryPointer.new(:ulong_long)
283
- total = FFI::MemoryPointer.new(:ulong_long)
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, sectors, bytes, free, total)
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
- sectors = sectors.read_ulong_long
291
- bytes = bytes.read_ulong_long
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
- block_size = sectors * bytes
296
- blocks_avail = total_bytes / block_size
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('GetVolumInformation', FFI.errno)
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, blocks_avail)
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
- private
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
- results = wmi.ExecQuery(query)
361
- results.each{ |ole|
362
- time_array = Time.parse(ole.LastBootupTime.split('.').first)
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
- str = ""
374
- str << " casepres" if CASE_PRESERVED_NAMES & flags > 0
375
- str << " casesens" if CASE_SENSITIVE_SEARCH & flags > 0
376
- str << " compression" if FILE_COMPRESSION & flags > 0
377
- str << " namedstreams" if NAMED_STREAMS & flags > 0
378
- str << " pacls" if PERSISTENT_ACLS & flags > 0
379
- str << " ro" if READ_ONLY_VOLUME & flags > 0
380
- str << " encryption" if SUPPORTS_ENCRYPTION & flags > 0
381
- str << " objids" if SUPPORTS_OBJECT_IDS & flags > 0
382
- str << " rpoints" if SUPPORTS_REPARSE_POINTS & flags > 0
383
- str << " sparse" if SUPPORTS_SPARSE_FILES & flags > 0
384
- str << " unicode" if UNICODE_ON_DISK & flags > 0
385
- str << " compressed" if VOLUME_IS_COMPRESSED & flags > 0
386
-
387
- str.tr!(' ', ',')
388
- str = str[1..-1] # Ignore the first comma
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
- # call-seq:
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
@@ -0,0 +1,6 @@
1
+ require 'rspec'
2
+
3
+ RSpec.configure do |config|
4
+ config.filter_run_excluding(:windows) unless Gem.win_platform?
5
+ config.filter_run_excluding(:unix) if Gem.win_platform?
6
+ end