/Ruby/lib/ruby/gems/2.0.0/gems/sys-filesystem-1.1.3/lib/windows/sys/filesystem.rb
https://gitlab.com/orvi2014/rcs-db-ext · Ruby · 435 lines · 240 code · 74 blank · 121 comment · 23 complexity · 2847763fa4ce8d38138eb959a2e675ee MD5 · raw file
- require File.join(File.dirname(__FILE__), 'filesystem', 'constants')
- require File.join(File.dirname(__FILE__), 'filesystem', 'functions')
- require File.join(File.dirname(__FILE__), 'filesystem', 'helper')
- require 'socket'
- require 'win32ole'
- require 'date'
- require 'time'
- # The Sys module serves as a namespace only.
- module Sys
- # The Filesystem class encapsulates information about your filesystem.
- class Filesystem
- include Sys::Filesystem::Constants
- extend Sys::Filesystem::Functions
- # Error typically raised if any of the Sys::Filesystem methods fail.
- class Error < StandardError; end
- # The version of the sys-filesystem library.
- VERSION = '1.1.3'
- class Mount
- # The name of the volume. This is the device mapping.
- attr_reader :name
- # The last time the volume was mounted. For MS Windows this equates
- # to your system's boot time.
- attr_reader :mount_time
- # The type of mount, e.g. NTFS, UDF, etc.
- attr_reader :mount_type
- # The volume mount point, e.g. 'C:\'
- attr_reader :mount_point
- # Various comma separated options that reflect the volume's features
- attr_reader :options
- # Always nil on MS Windows. Provided for interface compatibility only.
- attr_reader :pass_number
- # Always nil on MS Windows. Provided for interface compatibility only.
- attr_reader :frequency
- alias fsname name
- alias dir mount_point
- alias opts options
- alias passno pass_number
- alias freq frequency
- end
- class Stat
- # The path of the file system.
- attr_reader :path
- # The file system block size. MS Windows typically defaults to 4096.
- attr_reader :block_size
- # Fragment size. Meaningless at the moment.
- attr_reader :fragment_size
- # The total number of blocks available (used or unused) on the file
- # system.
- attr_reader :blocks
- # The total number of unused blocks.
- attr_reader :blocks_free
- # The total number of unused blocks available to unprivileged
- # processes. Identical to +blocks+ at the moment.
- attr_reader :blocks_available
- # Total number of files/inodes that can be created on the file system.
- # This attribute is always nil on MS Windows.
- attr_reader :files
- # Total number of free files/inodes that can be created on the file
- # system. This attribute is always nil on MS Windows.
- attr_reader :files_free
- # Total number of available files/inodes for unprivileged processes
- # that can be created on the file system. This attribute is always
- # nil on MS Windows.
- attr_reader :files_available
- # The file system volume id.
- attr_reader :filesystem_id
- # A bit mask of file system flags.
- attr_reader :flags
- # The maximum length of a file name permitted on the file system.
- attr_reader :name_max
- # The file system type, e.g. NTFS, FAT, etc.
- attr_reader :base_type
- # Returns the total amount of free space on the partition.
- attr_reader :bytes_free
- alias inodes files
- alias inodes_free files_free
- alias inodes_available files_available
- # Returns the total space on the partition.
- def bytes_total
- blocks * block_size
- end
- # Returns the total amount of used space on the partition.
- def bytes_used
- bytes_total - bytes_free
- end
- # Returns the percentage of the partition that has been used.
- def percent_used
- 100 - (100.0 * bytes_free.to_f / bytes_total.to_f)
- end
- end
- # Yields a Filesystem::Mount object for each volume on your system in
- # block form. Returns an array of Filesystem::Mount objects in non-block
- # form.
- #
- # Example:
- #
- # Sys::Filesystem.mounts{ |mount|
- # p mt.name # => \\Device\\HarddiskVolume1
- # p mt.mount_point # => C:\
- # p mt.mount_time # => Thu Dec 18 20:12:08 -0700 2008
- # p mt.mount_type # => NTFS
- # p mt.options # => casepres,casesens,ro,unicode
- # p mt.pass_number # => nil
- # p mt.dump_freq # => nil
- # }
- #
- # This method is a bit of a fudge for MS Windows in the name of interface
- # compatibility because this method deals with volumes, not actual mount
- # points. But, I believe it provides the sort of information many users
- # want at a glance.
- #
- # The possible values for the +options+ and their meanings are as follows:
- #
- # casepres => The filesystem preserves the case of file names when it places a name on disk.
- # casesens => The filesystem supports case-sensitive file names.
- # compression => The filesystem supports file-based compression.
- # namedstreams => The filesystem supports named streams.
- # pacls => The filesystem preserves and enforces access control lists.
- # ro => The filesystem is read-only.
- # encryption => The filesystem supports the Encrypted File System (EFS).
- # objids => The filesystem supports object identifiers.
- # rpoints => The filesystem supports reparse points.
- # sparse => The filesystem supports sparse files.
- # unicode => The filesystem supports Unicode in file names as they appear on disk.
- # compressed => The filesystem is compressed.
- #
- #--
- # I couldn't really find a good reason to use the wide functions for this
- # method. If you have one, patches welcome.
- #
- def self.mounts
- # First call, get needed buffer size
- buffer = 0.chr
- length = GetLogicalDriveStringsA(buffer.size, buffer)
- if length == 0
- raise SystemCallError.new('GetLogicalDriveStrings', FFI.errno)
- else
- buffer = 0.chr * length
- end
- mounts = block_given? ? nil : []
- # Try again with new buffer size
- if GetLogicalDriveStringsA(buffer.size, buffer) == 0
- raise SystemCallError.new('GetLogicalDriveStrings', FFI.errno)
- end
- drives = buffer.split(0.chr)
- boot_time = get_boot_time
- drives.each{ |drive|
- mount = Mount.new
- volume = FFI::MemoryPointer.new(:char, MAXPATH)
- fsname = FFI::MemoryPointer.new(:char, MAXPATH)
- mount.instance_variable_set(:@mount_point, drive)
- mount.instance_variable_set(:@mount_time, boot_time)
- volume_serial_number = FFI::MemoryPointer.new(:ulong)
- max_component_length = FFI::MemoryPointer.new(:ulong)
- filesystem_flags = FFI::MemoryPointer.new(:ulong)
- bool = GetVolumeInformationA(
- drive,
- volume,
- volume.size,
- volume_serial_number,
- max_component_length,
- filesystem_flags,
- fsname,
- fsname.size
- )
- # Skip unmounted floppies or cd-roms, or inaccessible drives
- unless bool
- if [5,21].include?(FFI.errno) # ERROR_NOT_READY or ERROR_ACCESS_DENIED
- next
- else
- raise SystemCallError.new('GetVolumeInformation', FFI.errno)
- end
- end
- filesystem_flags = filesystem_flags.read_ulong
- fsname = fsname.read_string
- name = 0.chr * MAXPATH
- if QueryDosDeviceA(drive[0,2], name, name.size) == 0
- raise SystemCallError.new('QueryDosDevice', FFI.errno)
- end
- mount.instance_variable_set(:@name, name.strip)
- mount.instance_variable_set(:@mount_type, fsname)
- mount.instance_variable_set(:@options, get_options(filesystem_flags))
- if block_given?
- yield mount
- else
- mounts << mount
- end
- }
- mounts # Nil if the block form was used.
- end
- # Returns the mount point for the given +file+. For MS Windows this
- # means the root of the path.
- #
- # Example:
- #
- # File.mount_point("C:\\Documents and Settings") # => "C:\\'
- #
- def self.mount_point(file)
- wfile = FFI::MemoryPointer.from_string(file.wincode)
- if PathStripToRootW(wfile)
- wfile.read_string(wfile.size).split("\000\000").first.tr(0.chr, '')
- else
- nil
- end
- end
- # Returns a Filesystem::Stat object that contains information about the
- # +path+ file system. On Windows this will default to using the root
- # path for volume information.
- #
- # Examples:
- #
- # Sys::Filesystem.stat("C:\\")
- # Sys::Filesystem.stat("C:\\Documents and Settings\\some_user")
- #
- def self.stat(path)
- bytes_avail = FFI::MemoryPointer.new(:ulong_long)
- bytes_free = FFI::MemoryPointer.new(:ulong_long)
- total_bytes = FFI::MemoryPointer.new(:ulong_long)
- mpoint = mount_point(path)
- wpath = path.wincode
- # We need this call for the 64 bit support
- unless GetDiskFreeSpaceExW(wpath, bytes_avail, total_bytes, bytes_free)
- raise SystemCallError.new('GetDiskFreeSpaceEx', FFI.errno)
- end
- bytes_avail = bytes_avail.read_ulong_long
- bytes_free = bytes_free.read_ulong_long
- total_bytes = total_bytes.read_ulong_long
- sectors = FFI::MemoryPointer.new(:ulong_long)
- bytes = FFI::MemoryPointer.new(:ulong_long)
- free = FFI::MemoryPointer.new(:ulong_long)
- total = FFI::MemoryPointer.new(:ulong_long)
- # We need this call for the total/cluster info, which is not in the Ex call.
- unless GetDiskFreeSpaceW(wpath, sectors, bytes, free, total)
- raise SystemCallError.new('GetDiskFreeSpace', FFI.errno)
- end
- sectors = sectors.read_ulong_long
- bytes = bytes.read_ulong_long
- free = free.read_ulong_long
- total = total.read_ulong_long
- block_size = sectors * bytes
- blocks_avail = total_bytes / block_size
- blocks_free = bytes_free / block_size
- vol_name_ptr = FFI::MemoryPointer.new(:char, MAXPATH)
- base_type_ptr = FFI::MemoryPointer.new(:char, MAXPATH)
- vol_serial_ptr = FFI::MemoryPointer.new(:ulong)
- name_max_ptr = FFI::MemoryPointer.new(:ulong)
- flags_ptr = FFI::MemoryPointer.new(:ulong)
- bool = GetVolumeInformationW(
- mpoint.wincode,
- vol_name_ptr,
- vol_name_ptr.size,
- vol_serial_ptr,
- name_max_ptr,
- flags_ptr,
- base_type_ptr,
- base_type_ptr.size
- )
- unless bool
- raise SystemCallError.new('GetVolumInformation', FFI.errno)
- end
- vol_serial = vol_serial_ptr.read_ulong
- name_max = name_max_ptr.read_ulong
- flags = flags_ptr.read_ulong
- base_type = base_type_ptr.read_string(base_type_ptr.size).tr(0.chr, '')
- vol_name_ptr.free
- vol_serial_ptr.free
- name_max_ptr.free
- flags_ptr.free
- base_type_ptr.free
- stat_obj = Stat.new
- stat_obj.instance_variable_set(:@path, path)
- stat_obj.instance_variable_set(:@block_size, block_size)
- stat_obj.instance_variable_set(:@blocks, blocks_avail)
- stat_obj.instance_variable_set(:@blocks_available, blocks_avail)
- stat_obj.instance_variable_set(:@blocks_free, blocks_free)
- stat_obj.instance_variable_set(:@name_max, name_max)
- stat_obj.instance_variable_set(:@base_type, base_type)
- stat_obj.instance_variable_set(:@flags, flags)
- stat_obj.instance_variable_set(:@filesystem_id, vol_serial)
- stat_obj.instance_variable_set(:@bytes_free, bytes_free)
- stat_obj.freeze # Read-only object
- end
- private
- # This method is used to get the boot time of the system, which is used
- # for the mount_time attribute within the File.mounts method.
- #
- def self.get_boot_time
- host = Socket.gethostname
- cs = "winmgmts://#{host}/root/cimv2"
- begin
- wmi = WIN32OLE.connect(cs)
- rescue WIN32OLERuntimeError => e
- raise Error, e
- else
- query = 'select LastBootupTime from Win32_OperatingSystem'
- results = wmi.ExecQuery(query)
- results.each{ |ole|
- time_array = Time.parse(ole.LastBootupTime.split('.').first)
- return Time.mktime(*time_array)
- }
- end
- end
- # Private method that converts filesystem flags into a comma separated
- # list of strings. The presentation is meant as a rough analogue to the
- # way options are presented for Unix filesystems.
- #
- def self.get_options(flags)
- str = ""
- str << " casepres" if CASE_PRESERVED_NAMES & flags > 0
- str << " casesens" if CASE_SENSITIVE_SEARCH & flags > 0
- str << " compression" if FILE_COMPRESSION & flags > 0
- str << " namedstreams" if NAMED_STREAMS & flags > 0
- str << " pacls" if PERSISTENT_ACLS & flags > 0
- str << " ro" if READ_ONLY_VOLUME & flags > 0
- str << " encryption" if SUPPORTS_ENCRYPTION & flags > 0
- str << " objids" if SUPPORTS_OBJECT_IDS & flags > 0
- str << " rpoints" if SUPPORTS_REPARSE_POINTS & flags > 0
- str << " sparse" if SUPPORTS_SPARSE_FILES & flags > 0
- str << " unicode" if UNICODE_ON_DISK & flags > 0
- str << " compressed" if VOLUME_IS_COMPRESSED & flags > 0
- str.tr!(' ', ',')
- str = str[1..-1] # Ignore the first comma
- str
- end
- end
- end
- # Some convenient methods for converting bytes to kb, mb, and gb.
- #
- class Numeric
- # call-seq:
- # <tt>num</tt>.to_kb
- #
- # Returns +num+ in terms of kilobytes.
- def to_kb
- self / 1024
- end
- # call-seq:
- # <tt>num</tt>.to_mb
- #
- # Returns +num+ in terms of megabytes.
- def to_mb
- self / 1048576
- end
- # call-seq:
- # <tt>num</tt>.to_gb
- #
- # Returns +num+ in terms of gigabytes.
- def to_gb
- self / 1073741824
- end
- # call-seq:
- # <tt>num</tt>.to_gb
- #
- # Returns +num+ in terms of terabytes.
- def to_tb
- self / 1099511627776
- end
- end
- if $0 == __FILE__
- p Sys::Filesystem.stat("C:/Users/djberge")
- end