PageRenderTime 53ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 0ms

/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
  1. require File.join(File.dirname(__FILE__), 'filesystem', 'constants')
  2. require File.join(File.dirname(__FILE__), 'filesystem', 'functions')
  3. require File.join(File.dirname(__FILE__), 'filesystem', 'helper')
  4. require 'socket'
  5. require 'win32ole'
  6. require 'date'
  7. require 'time'
  8. # The Sys module serves as a namespace only.
  9. module Sys
  10. # The Filesystem class encapsulates information about your filesystem.
  11. class Filesystem
  12. include Sys::Filesystem::Constants
  13. extend Sys::Filesystem::Functions
  14. # Error typically raised if any of the Sys::Filesystem methods fail.
  15. class Error < StandardError; end
  16. # The version of the sys-filesystem library.
  17. VERSION = '1.1.3'
  18. class Mount
  19. # The name of the volume. This is the device mapping.
  20. attr_reader :name
  21. # The last time the volume was mounted. For MS Windows this equates
  22. # to your system's boot time.
  23. attr_reader :mount_time
  24. # The type of mount, e.g. NTFS, UDF, etc.
  25. attr_reader :mount_type
  26. # The volume mount point, e.g. 'C:\'
  27. attr_reader :mount_point
  28. # Various comma separated options that reflect the volume's features
  29. attr_reader :options
  30. # Always nil on MS Windows. Provided for interface compatibility only.
  31. attr_reader :pass_number
  32. # Always nil on MS Windows. Provided for interface compatibility only.
  33. attr_reader :frequency
  34. alias fsname name
  35. alias dir mount_point
  36. alias opts options
  37. alias passno pass_number
  38. alias freq frequency
  39. end
  40. class Stat
  41. # The path of the file system.
  42. attr_reader :path
  43. # The file system block size. MS Windows typically defaults to 4096.
  44. attr_reader :block_size
  45. # Fragment size. Meaningless at the moment.
  46. attr_reader :fragment_size
  47. # The total number of blocks available (used or unused) on the file
  48. # system.
  49. attr_reader :blocks
  50. # The total number of unused blocks.
  51. attr_reader :blocks_free
  52. # The total number of unused blocks available to unprivileged
  53. # processes. Identical to +blocks+ at the moment.
  54. attr_reader :blocks_available
  55. # Total number of files/inodes that can be created on the file system.
  56. # This attribute is always nil on MS Windows.
  57. attr_reader :files
  58. # Total number of free files/inodes that can be created on the file
  59. # system. This attribute is always nil on MS Windows.
  60. attr_reader :files_free
  61. # Total number of available files/inodes for unprivileged processes
  62. # that can be created on the file system. This attribute is always
  63. # nil on MS Windows.
  64. attr_reader :files_available
  65. # The file system volume id.
  66. attr_reader :filesystem_id
  67. # A bit mask of file system flags.
  68. attr_reader :flags
  69. # The maximum length of a file name permitted on the file system.
  70. attr_reader :name_max
  71. # The file system type, e.g. NTFS, FAT, etc.
  72. attr_reader :base_type
  73. # Returns the total amount of free space on the partition.
  74. attr_reader :bytes_free
  75. alias inodes files
  76. alias inodes_free files_free
  77. alias inodes_available files_available
  78. # Returns the total space on the partition.
  79. def bytes_total
  80. blocks * block_size
  81. end
  82. # Returns the total amount of used space on the partition.
  83. def bytes_used
  84. bytes_total - bytes_free
  85. end
  86. # Returns the percentage of the partition that has been used.
  87. def percent_used
  88. 100 - (100.0 * bytes_free.to_f / bytes_total.to_f)
  89. end
  90. end
  91. # Yields a Filesystem::Mount object for each volume on your system in
  92. # block form. Returns an array of Filesystem::Mount objects in non-block
  93. # form.
  94. #
  95. # Example:
  96. #
  97. # Sys::Filesystem.mounts{ |mount|
  98. # p mt.name # => \\Device\\HarddiskVolume1
  99. # p mt.mount_point # => C:\
  100. # p mt.mount_time # => Thu Dec 18 20:12:08 -0700 2008
  101. # p mt.mount_type # => NTFS
  102. # p mt.options # => casepres,casesens,ro,unicode
  103. # p mt.pass_number # => nil
  104. # p mt.dump_freq # => nil
  105. # }
  106. #
  107. # This method is a bit of a fudge for MS Windows in the name of interface
  108. # compatibility because this method deals with volumes, not actual mount
  109. # points. But, I believe it provides the sort of information many users
  110. # want at a glance.
  111. #
  112. # The possible values for the +options+ and their meanings are as follows:
  113. #
  114. # casepres => The filesystem preserves the case of file names when it places a name on disk.
  115. # casesens => The filesystem supports case-sensitive file names.
  116. # compression => The filesystem supports file-based compression.
  117. # namedstreams => The filesystem supports named streams.
  118. # pacls => The filesystem preserves and enforces access control lists.
  119. # ro => The filesystem is read-only.
  120. # encryption => The filesystem supports the Encrypted File System (EFS).
  121. # objids => The filesystem supports object identifiers.
  122. # rpoints => The filesystem supports reparse points.
  123. # sparse => The filesystem supports sparse files.
  124. # unicode => The filesystem supports Unicode in file names as they appear on disk.
  125. # compressed => The filesystem is compressed.
  126. #
  127. #--
  128. # I couldn't really find a good reason to use the wide functions for this
  129. # method. If you have one, patches welcome.
  130. #
  131. def self.mounts
  132. # First call, get needed buffer size
  133. buffer = 0.chr
  134. length = GetLogicalDriveStringsA(buffer.size, buffer)
  135. if length == 0
  136. raise SystemCallError.new('GetLogicalDriveStrings', FFI.errno)
  137. else
  138. buffer = 0.chr * length
  139. end
  140. mounts = block_given? ? nil : []
  141. # Try again with new buffer size
  142. if GetLogicalDriveStringsA(buffer.size, buffer) == 0
  143. raise SystemCallError.new('GetLogicalDriveStrings', FFI.errno)
  144. end
  145. drives = buffer.split(0.chr)
  146. boot_time = get_boot_time
  147. drives.each{ |drive|
  148. mount = Mount.new
  149. volume = FFI::MemoryPointer.new(:char, MAXPATH)
  150. fsname = FFI::MemoryPointer.new(:char, MAXPATH)
  151. mount.instance_variable_set(:@mount_point, drive)
  152. mount.instance_variable_set(:@mount_time, boot_time)
  153. volume_serial_number = FFI::MemoryPointer.new(:ulong)
  154. max_component_length = FFI::MemoryPointer.new(:ulong)
  155. filesystem_flags = FFI::MemoryPointer.new(:ulong)
  156. bool = GetVolumeInformationA(
  157. drive,
  158. volume,
  159. volume.size,
  160. volume_serial_number,
  161. max_component_length,
  162. filesystem_flags,
  163. fsname,
  164. fsname.size
  165. )
  166. # Skip unmounted floppies or cd-roms, or inaccessible drives
  167. unless bool
  168. if [5,21].include?(FFI.errno) # ERROR_NOT_READY or ERROR_ACCESS_DENIED
  169. next
  170. else
  171. raise SystemCallError.new('GetVolumeInformation', FFI.errno)
  172. end
  173. end
  174. filesystem_flags = filesystem_flags.read_ulong
  175. fsname = fsname.read_string
  176. name = 0.chr * MAXPATH
  177. if QueryDosDeviceA(drive[0,2], name, name.size) == 0
  178. raise SystemCallError.new('QueryDosDevice', FFI.errno)
  179. end
  180. mount.instance_variable_set(:@name, name.strip)
  181. mount.instance_variable_set(:@mount_type, fsname)
  182. mount.instance_variable_set(:@options, get_options(filesystem_flags))
  183. if block_given?
  184. yield mount
  185. else
  186. mounts << mount
  187. end
  188. }
  189. mounts # Nil if the block form was used.
  190. end
  191. # Returns the mount point for the given +file+. For MS Windows this
  192. # means the root of the path.
  193. #
  194. # Example:
  195. #
  196. # File.mount_point("C:\\Documents and Settings") # => "C:\\'
  197. #
  198. def self.mount_point(file)
  199. wfile = FFI::MemoryPointer.from_string(file.wincode)
  200. if PathStripToRootW(wfile)
  201. wfile.read_string(wfile.size).split("\000\000").first.tr(0.chr, '')
  202. else
  203. nil
  204. end
  205. end
  206. # Returns a Filesystem::Stat object that contains information about the
  207. # +path+ file system. On Windows this will default to using the root
  208. # path for volume information.
  209. #
  210. # Examples:
  211. #
  212. # Sys::Filesystem.stat("C:\\")
  213. # Sys::Filesystem.stat("C:\\Documents and Settings\\some_user")
  214. #
  215. def self.stat(path)
  216. bytes_avail = FFI::MemoryPointer.new(:ulong_long)
  217. bytes_free = FFI::MemoryPointer.new(:ulong_long)
  218. total_bytes = FFI::MemoryPointer.new(:ulong_long)
  219. mpoint = mount_point(path)
  220. wpath = path.wincode
  221. # We need this call for the 64 bit support
  222. unless GetDiskFreeSpaceExW(wpath, bytes_avail, total_bytes, bytes_free)
  223. raise SystemCallError.new('GetDiskFreeSpaceEx', FFI.errno)
  224. end
  225. bytes_avail = bytes_avail.read_ulong_long
  226. bytes_free = bytes_free.read_ulong_long
  227. total_bytes = total_bytes.read_ulong_long
  228. sectors = FFI::MemoryPointer.new(:ulong_long)
  229. bytes = FFI::MemoryPointer.new(:ulong_long)
  230. free = FFI::MemoryPointer.new(:ulong_long)
  231. total = FFI::MemoryPointer.new(:ulong_long)
  232. # We need this call for the total/cluster info, which is not in the Ex call.
  233. unless GetDiskFreeSpaceW(wpath, sectors, bytes, free, total)
  234. raise SystemCallError.new('GetDiskFreeSpace', FFI.errno)
  235. end
  236. sectors = sectors.read_ulong_long
  237. bytes = bytes.read_ulong_long
  238. free = free.read_ulong_long
  239. total = total.read_ulong_long
  240. block_size = sectors * bytes
  241. blocks_avail = total_bytes / block_size
  242. blocks_free = bytes_free / block_size
  243. vol_name_ptr = FFI::MemoryPointer.new(:char, MAXPATH)
  244. base_type_ptr = FFI::MemoryPointer.new(:char, MAXPATH)
  245. vol_serial_ptr = FFI::MemoryPointer.new(:ulong)
  246. name_max_ptr = FFI::MemoryPointer.new(:ulong)
  247. flags_ptr = FFI::MemoryPointer.new(:ulong)
  248. bool = GetVolumeInformationW(
  249. mpoint.wincode,
  250. vol_name_ptr,
  251. vol_name_ptr.size,
  252. vol_serial_ptr,
  253. name_max_ptr,
  254. flags_ptr,
  255. base_type_ptr,
  256. base_type_ptr.size
  257. )
  258. unless bool
  259. raise SystemCallError.new('GetVolumInformation', FFI.errno)
  260. end
  261. vol_serial = vol_serial_ptr.read_ulong
  262. name_max = name_max_ptr.read_ulong
  263. flags = flags_ptr.read_ulong
  264. base_type = base_type_ptr.read_string(base_type_ptr.size).tr(0.chr, '')
  265. vol_name_ptr.free
  266. vol_serial_ptr.free
  267. name_max_ptr.free
  268. flags_ptr.free
  269. base_type_ptr.free
  270. stat_obj = Stat.new
  271. stat_obj.instance_variable_set(:@path, path)
  272. stat_obj.instance_variable_set(:@block_size, block_size)
  273. stat_obj.instance_variable_set(:@blocks, blocks_avail)
  274. stat_obj.instance_variable_set(:@blocks_available, blocks_avail)
  275. stat_obj.instance_variable_set(:@blocks_free, blocks_free)
  276. stat_obj.instance_variable_set(:@name_max, name_max)
  277. stat_obj.instance_variable_set(:@base_type, base_type)
  278. stat_obj.instance_variable_set(:@flags, flags)
  279. stat_obj.instance_variable_set(:@filesystem_id, vol_serial)
  280. stat_obj.instance_variable_set(:@bytes_free, bytes_free)
  281. stat_obj.freeze # Read-only object
  282. end
  283. private
  284. # This method is used to get the boot time of the system, which is used
  285. # for the mount_time attribute within the File.mounts method.
  286. #
  287. def self.get_boot_time
  288. host = Socket.gethostname
  289. cs = "winmgmts://#{host}/root/cimv2"
  290. begin
  291. wmi = WIN32OLE.connect(cs)
  292. rescue WIN32OLERuntimeError => e
  293. raise Error, e
  294. else
  295. query = 'select LastBootupTime from Win32_OperatingSystem'
  296. results = wmi.ExecQuery(query)
  297. results.each{ |ole|
  298. time_array = Time.parse(ole.LastBootupTime.split('.').first)
  299. return Time.mktime(*time_array)
  300. }
  301. end
  302. end
  303. # Private method that converts filesystem flags into a comma separated
  304. # list of strings. The presentation is meant as a rough analogue to the
  305. # way options are presented for Unix filesystems.
  306. #
  307. def self.get_options(flags)
  308. str = ""
  309. str << " casepres" if CASE_PRESERVED_NAMES & flags > 0
  310. str << " casesens" if CASE_SENSITIVE_SEARCH & flags > 0
  311. str << " compression" if FILE_COMPRESSION & flags > 0
  312. str << " namedstreams" if NAMED_STREAMS & flags > 0
  313. str << " pacls" if PERSISTENT_ACLS & flags > 0
  314. str << " ro" if READ_ONLY_VOLUME & flags > 0
  315. str << " encryption" if SUPPORTS_ENCRYPTION & flags > 0
  316. str << " objids" if SUPPORTS_OBJECT_IDS & flags > 0
  317. str << " rpoints" if SUPPORTS_REPARSE_POINTS & flags > 0
  318. str << " sparse" if SUPPORTS_SPARSE_FILES & flags > 0
  319. str << " unicode" if UNICODE_ON_DISK & flags > 0
  320. str << " compressed" if VOLUME_IS_COMPRESSED & flags > 0
  321. str.tr!(' ', ',')
  322. str = str[1..-1] # Ignore the first comma
  323. str
  324. end
  325. end
  326. end
  327. # Some convenient methods for converting bytes to kb, mb, and gb.
  328. #
  329. class Numeric
  330. # call-seq:
  331. # <tt>num</tt>.to_kb
  332. #
  333. # Returns +num+ in terms of kilobytes.
  334. def to_kb
  335. self / 1024
  336. end
  337. # call-seq:
  338. # <tt>num</tt>.to_mb
  339. #
  340. # Returns +num+ in terms of megabytes.
  341. def to_mb
  342. self / 1048576
  343. end
  344. # call-seq:
  345. # <tt>num</tt>.to_gb
  346. #
  347. # Returns +num+ in terms of gigabytes.
  348. def to_gb
  349. self / 1073741824
  350. end
  351. # call-seq:
  352. # <tt>num</tt>.to_gb
  353. #
  354. # Returns +num+ in terms of terabytes.
  355. def to_tb
  356. self / 1099511627776
  357. end
  358. end
  359. if $0 == __FILE__
  360. p Sys::Filesystem.stat("C:/Users/djberge")
  361. end