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.
@@ -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