sys-filesystem 0.3.4-x86-mingw32 → 1.0.0-x86-mingw32

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.
@@ -0,0 +1,561 @@
1
+ require 'ffi'
2
+ require 'rbconfig'
3
+
4
+ # The Sys module serves as a namespace only.
5
+ module Sys
6
+ # The Filesystem class serves as an abstract base class. Its methods
7
+ # return objects of other types. Do not instantiate.
8
+ class Filesystem
9
+ extend FFI::Library
10
+ ffi_lib(FFI::Library::LIBC)
11
+
12
+ # The version of the sys-filesystem library.
13
+ VERSION = '1.0.0'
14
+
15
+ private
16
+
17
+ attach_function(:statvfs, [:string, :pointer], :int)
18
+ attach_function(:strerror, [:int], :string)
19
+
20
+ private_class_method :statvfs, :strerror
21
+
22
+ begin
23
+ if RbConfig::CONFIG['host_os'] =~ /sunos|solaris/i
24
+ attach_function(:fopen, [:string, :string], :pointer)
25
+ attach_function(:fclose, [:pointer], :int)
26
+ attach_function(:getmntent, [:pointer, :pointer], :int)
27
+ private_class_method :fopen, :fclose, :getmntent
28
+ else
29
+ attach_function(:getmntent, [:pointer], :pointer)
30
+ attach_function(:setmntent, [:string, :string], :pointer)
31
+ attach_function(:endmntent, [:pointer], :int)
32
+ private_class_method :getmntent, :setmntent, :endmntent
33
+ end
34
+ rescue FFI::NotFoundError
35
+ if RbConfig::CONFIG['host_os'] =~ /darwin|osx|mach/i
36
+ attach_function(:getmntinfo, :getmntinfo64, [:pointer, :int], :int)
37
+ else
38
+ attach_function(:getmntinfo, [:pointer, :int], :int)
39
+ end
40
+ private_class_method :getmntinfo
41
+ end
42
+
43
+ MNT_RDONLY = 0x00000001 # read only filesystem
44
+ MNT_SYNCHRONOUS = 0x00000002 # file system written synchronously
45
+ MNT_NOEXEC = 0x00000004 # can't exec from filesystem
46
+ MNT_NOSUID = 0x00000008 # don't honor setuid bits on fs
47
+ MNT_NODEV = 0x00000010 # don't interpret special files
48
+ MNT_UNION = 0x00000020 # union with underlying filesystem
49
+ MNT_ASYNC = 0x00000040 # file system written asynchronously
50
+ MNT_CPROTECT = 0x00000080 # file system supports content protection
51
+ MNT_EXPORTED = 0x00000100 # file system is exported
52
+ MNT_QUARANTINE = 0x00000400 # file system is quarantined
53
+ MNT_LOCAL = 0x00001000 # filesystem is stored locally
54
+ MNT_QUOTA = 0x00002000 # quotas are enabled on filesystem
55
+ MNT_ROOTFS = 0x00004000 # identifies the root filesystem
56
+ MNT_DOVOLFS = 0x00008000 # FS supports volfs (deprecated)
57
+ MNT_DONTBROWSE = 0x00100000 # FS is not appropriate path to user data
58
+ MNT_IGNORE_OWNERSHIP = 0x00200000 # VFS will ignore ownership info on FS objects
59
+ MNT_AUTOMOUNTED = 0x00400000 # filesystem was mounted by automounter
60
+ MNT_JOURNALED = 0x00800000 # filesystem is journaled
61
+ MNT_NOUSERXATTR = 0x01000000 # Don't allow user extended attributes
62
+ MNT_DEFWRITE = 0x02000000 # filesystem should defer writes
63
+ MNT_MULTILABEL = 0x04000000 # MAC support for individual labels
64
+ MNT_NOATIME = 0x10000000 # disable update of file access time
65
+
66
+ MNT_VISFLAGMASK = (
67
+ MNT_RDONLY | MNT_SYNCHRONOUS | MNT_NOEXEC |
68
+ MNT_NOSUID | MNT_NODEV | MNT_UNION |
69
+ MNT_ASYNC | MNT_EXPORTED | MNT_QUARANTINE |
70
+ MNT_LOCAL | MNT_QUOTA |
71
+ MNT_ROOTFS | MNT_DOVOLFS | MNT_DONTBROWSE |
72
+ MNT_IGNORE_OWNERSHIP | MNT_AUTOMOUNTED | MNT_JOURNALED |
73
+ MNT_NOUSERXATTR | MNT_DEFWRITE | MNT_MULTILABEL |
74
+ MNT_NOATIME | MNT_CPROTECT
75
+ )
76
+
77
+ @@opt_names = {
78
+ MNT_RDONLY => 'read-only',
79
+ MNT_SYNCHRONOUS => 'synchronous',
80
+ MNT_NOEXEC => 'noexec',
81
+ MNT_NOSUID => 'nosuid',
82
+ MNT_NODEV => 'nodev',
83
+ MNT_UNION => 'union',
84
+ MNT_ASYNC => 'asynchronous',
85
+ MNT_CPROTECT => 'content-protection',
86
+ MNT_EXPORTED => 'exported',
87
+ MNT_QUARANTINE => 'quarantined',
88
+ MNT_LOCAL => 'local',
89
+ MNT_QUOTA => 'quotas',
90
+ MNT_ROOTFS => 'rootfs',
91
+ MNT_DONTBROWSE => 'nobrowse',
92
+ MNT_IGNORE_OWNERSHIP => 'noowners',
93
+ MNT_AUTOMOUNTED => 'automounted',
94
+ MNT_JOURNALED => 'journaled',
95
+ MNT_NOUSERXATTR => 'nouserxattr',
96
+ MNT_DEFWRITE => 'defwrite',
97
+ MNT_NOATIME => 'noatime'
98
+ }
99
+
100
+ # File used to read mount informtion from.
101
+ if File.exists?('/etc/mtab')
102
+ MOUNT_FILE = '/etc/mtab'
103
+ elsif File.exists?('/etc/mnttab')
104
+ MOUNT_FILE = '/etc/mnttab'
105
+ else
106
+ MOUNT_FILE = 'getmntinfo'
107
+ end
108
+
109
+ class Statfs < FFI::Struct
110
+ if RbConfig::CONFIG['host_os'] =~ /bsd/i
111
+ layout(
112
+ :f_version, :uint32,
113
+ :f_type, :uint32,
114
+ :f_flags, :uint64,
115
+ :f_bsize, :uint64,
116
+ :f_iosize, :int64,
117
+ :f_blocks, :uint64,
118
+ :f_bfree, :uint64,
119
+ :f_bavail, :int64,
120
+ :f_files, :uint64,
121
+ :f_ffree, :uint64,
122
+ :f_syncwrites, :uint64,
123
+ :f_asyncwrites, :uint64,
124
+ :f_syncreads, :uint64,
125
+ :f_asyncreads, :uint64,
126
+ :f_spare, [:uint64, 10],
127
+ :f_namemax, :uint32,
128
+ :f_owner, :int32,
129
+ :f_fsid, [:int32, 2],
130
+ :f_charspare, [:char, 80],
131
+ :f_fstypename, [:char, 16],
132
+ :f_mntfromname, [:char, 88],
133
+ :f_mntonname, [:char, 88]
134
+ )
135
+ else
136
+ layout(
137
+ :f_bsize, :uint32,
138
+ :f_iosize, :int32,
139
+ :f_blocks, :uint64,
140
+ :f_bfree, :uint64,
141
+ :f_bavail, :uint64,
142
+ :f_files, :uint64,
143
+ :f_ffree, :uint64,
144
+ :f_fsid, [:int32, 2],
145
+ :f_owner, :int32,
146
+ :f_type, :uint32,
147
+ :f_flags, :uint32,
148
+ :f_fssubtype, :uint32,
149
+ :f_fstypename, [:char, 16],
150
+ :f_mntonname, [:char, 1024],
151
+ :f_mntfromname, [:char, 1024],
152
+ :f_reserved, [:uint32, 8]
153
+ )
154
+ end
155
+ end
156
+
157
+ # The Statvfs struct represents struct statvfs from sys/statvfs.h.
158
+ class Statvfs < FFI::Struct
159
+ if RbConfig::CONFIG['host_os'] =~ /darwin|osx|mach/i
160
+ layout(
161
+ :f_bsize, :ulong,
162
+ :f_frsize, :ulong,
163
+ :f_blocks, :uint,
164
+ :f_bfree, :uint,
165
+ :f_bavail, :uint,
166
+ :f_files, :uint,
167
+ :f_ffree, :uint,
168
+ :f_favail, :uint,
169
+ :f_fsid, :ulong,
170
+ :f_flag, :ulong,
171
+ :f_namemax, :ulong
172
+ )
173
+ elsif RbConfig::CONFIG['host'] =~ /bsd/i
174
+ layout(
175
+ :f_bavail, :uint64,
176
+ :f_bfree, :uint64,
177
+ :f_blocks, :uint64,
178
+ :f_favail, :uint64,
179
+ :f_ffree, :uint64,
180
+ :f_files, :uint64,
181
+ :f_bsize, :ulong,
182
+ :f_flag, :ulong,
183
+ :f_frsize, :ulong,
184
+ :f_fsid, :ulong,
185
+ :f_namemax, :ulong
186
+ )
187
+ elsif RbConfig::CONFIG['host'] =~ /sunos|solaris/i
188
+ layout(
189
+ :f_bsize, :ulong,
190
+ :f_frsize, :ulong,
191
+ :f_blocks, :ulong_t,
192
+ :f_bfree, :ulong_t,
193
+ :f_bavail, :ulong_t,
194
+ :f_files, :ulong_t,
195
+ :f_ffree, :ulong_t,
196
+ :f_favail, :ulong_t,
197
+ :f_fsid, :ulong,
198
+ :f_basetype, [:char, 16],
199
+ :f_flag, :uint32,
200
+ :f_namemax, :uint32,
201
+ :f_fstr, [:char, 32],
202
+ :f_filler, [:uint32, 16]
203
+ )
204
+ else
205
+ layout(
206
+ :f_bsize, :ulong,
207
+ :f_frsize, :ulong,
208
+ :f_blocks, :ulong,
209
+ :f_bfree, :ulong,
210
+ :f_bavail, :ulong,
211
+ :f_files, :ulong,
212
+ :f_ffree, :ulong,
213
+ :f_favail, :ulong,
214
+ :f_fsid, :ulong,
215
+ :f_flag, :ulong,
216
+ :f_namemax, :ulong,
217
+ :f_ftype, :ulong,
218
+ :f_basetype, [:char, 16],
219
+ :f_str, [:char, 16]
220
+ )
221
+ end
222
+ end
223
+
224
+ # The Mnttab struct represents struct mnnttab from sys/mnttab.h on Solaris.
225
+ class Mnttab < FFI::Struct
226
+ layout(
227
+ :mnt_special, :string,
228
+ :mnt_mountp, :string,
229
+ :mnt_fstype, :string,
230
+ :mnt_mntopts, :string,
231
+ :mnt_time, :string
232
+ )
233
+ end
234
+
235
+ # The Mntent struct represents struct mntent from sys/mount.h on Unix.
236
+ class Mntent < FFI::Struct
237
+ layout(
238
+ :mnt_fsname, :string,
239
+ :mnt_dir, :string,
240
+ :mnt_type, :string,
241
+ :mnt_opts, :string,
242
+ :mnt_freq, :int,
243
+ :mnt_passno, :int
244
+ )
245
+ end
246
+
247
+ public
248
+
249
+ # The error raised if any of the Filesystem methods fail.
250
+ class Error < StandardError; end
251
+
252
+ # Stat objects are returned by the Sys::Filesystem.stat method.
253
+ class Stat
254
+ # Read-only filesystem
255
+ RDONLY = 1
256
+
257
+ # Filesystem does not support suid or sgid semantics.
258
+ NOSUID = 2
259
+
260
+ # Filesystem does not truncate file names longer than +name_max+.
261
+ NOTRUNC = 3
262
+
263
+ # The path of the filesystem.
264
+ attr_accessor :path
265
+
266
+ # The preferred system block size.
267
+ attr_accessor :block_size
268
+
269
+ # The fragment size, i.e. fundamental filesystem block size.
270
+ attr_accessor :fragment_size
271
+
272
+ # The total number of +fragment_size+ blocks in the filesystem.
273
+ attr_accessor :blocks
274
+
275
+ # The total number of free blocks in the filesystem.
276
+ attr_accessor :blocks_free
277
+
278
+ # The number of free blocks available to unprivileged processes.
279
+ attr_accessor :blocks_available
280
+
281
+ # The total number of files/inodes that can be created.
282
+ attr_accessor :files
283
+
284
+ # The total number of files/inodes on the filesystem.
285
+ attr_accessor :files_free
286
+
287
+ # The number of free files/inodes available to unprivileged processes.
288
+ attr_accessor :files_available
289
+
290
+ # The filesystem identifier.
291
+ attr_accessor :filesystem_id
292
+
293
+ # A bit mask of flags.
294
+ attr_accessor :flags
295
+
296
+ # The maximum length of a file name permitted on the filesystem.
297
+ attr_accessor :name_max
298
+
299
+ # The filesystem type, e.g. UFS.
300
+ attr_accessor :base_type
301
+
302
+ alias inodes files
303
+ alias inodes_free files_free
304
+ alias inodes_available files_available
305
+
306
+ # Creates a new Sys::Filesystem::Stat object. This is meant for
307
+ # internal use only. Do not instantiate directly.
308
+ #
309
+ def initialize
310
+ @path = nil
311
+ @block_size = nil
312
+ @fragment_size = nil
313
+ @blocks = nil
314
+ @blocks_free = nil
315
+ @blocks_available = nil
316
+ @files = nil
317
+ @files_free = nil
318
+ @files_available = nil
319
+ @filesystem_id = nil
320
+ @flags = nil
321
+ @name_max = nil
322
+ @base_type = nil
323
+ end
324
+ end
325
+
326
+ # Mount objects are returned by the Sys::Filesystem.mounts method.
327
+ class Mount
328
+ # The name of the mounted resource.
329
+ attr_accessor :name
330
+
331
+ # The mount point/directory.
332
+ attr_accessor :mount_point
333
+
334
+ # The type of filesystem mount, e.g. ufs, nfs, etc.
335
+ attr_accessor :mount_type
336
+
337
+ # A list of comma separated options for the mount, e.g. nosuid, etc.
338
+ attr_accessor :options
339
+
340
+ # The time the filesystem was mounted. May be nil.
341
+ attr_accessor :mount_time
342
+
343
+ # The dump frequency in days. May be nil.
344
+ attr_accessor :dump_frequency
345
+
346
+ # The pass number of the filessytem check. May be nil.
347
+ attr_accessor :pass_number
348
+
349
+ alias fsname name
350
+ alias dir mount_point
351
+ alias opts options
352
+ alias passno pass_number
353
+ alias freq dump_frequency
354
+
355
+ # Creates a Sys::Filesystem::Mount object. This is meant for internal
356
+ # use only. Do no instantiate directly.
357
+ #
358
+ def initialize
359
+ @name = nil
360
+ @mount_point = nil
361
+ @mount_type = nil
362
+ @options = nil
363
+ @mount_time = nil
364
+ @dump_frequency = nil
365
+ @pass_number = nil
366
+ end
367
+ end
368
+
369
+ # Returns a Sys::Filesystem::Stat object containing information about the
370
+ # +path+ on the filesystem.
371
+ #
372
+ def self.stat(path)
373
+ fs = Statvfs.new
374
+
375
+ if statvfs(path, fs) < 0
376
+ raise Error, 'statvfs() function failed: ' + strerror(FFI.errno)
377
+ end
378
+
379
+ obj = Sys::Filesystem::Stat.new
380
+ obj.path = path
381
+ obj.block_size = fs[:f_bsize]
382
+ obj.fragment_size = fs[:f_frsize]
383
+ obj.blocks = fs[:f_blocks]
384
+ obj.blocks_free = fs[:f_bfree]
385
+ obj.blocks_available = fs[:f_bavail]
386
+ obj.files = fs[:f_files]
387
+ obj.files_free = fs[:f_ffree]
388
+ obj.files_available = fs[:f_favail]
389
+ obj.filesystem_id = fs[:f_fsid]
390
+ obj.flags = fs[:f_flag]
391
+ obj.name_max = fs[:f_namemax]
392
+
393
+ # OSX does things a little differently
394
+ if RbConfig::CONFIG['host_os'] =~ /darwin|osx|mach/i
395
+ obj.block_size /= 256
396
+ end
397
+
398
+ if fs.members.include?(:f_basetype)
399
+ obj.base_type = fs[:f_basetype].to_s
400
+ end
401
+
402
+ obj.freeze
403
+ end
404
+
405
+ # In block form, yields a Sys::Filesystem::Mount object for each mounted
406
+ # filesytem on the host. Otherwise it returns an array of Mount objects.
407
+ #
408
+ # Example:
409
+ #
410
+ # Sys::Filesystem.mounts{ |fs|
411
+ # p fs.name # => '/dev/dsk/c0t0d0s0'
412
+ # p fs.mount_time # => Thu Dec 11 15:07:23 -0700 2008
413
+ # p fs.mount_type # => 'ufs'
414
+ # p fs.mount_point # => '/'
415
+ # p fs.options # => local, noowner, nosuid
416
+ # }
417
+ #
418
+ def self.mounts
419
+ array = block_given? ? nil : []
420
+
421
+ if respond_to?(:getmntinfo, true)
422
+ buf = FFI::MemoryPointer.new(:pointer)
423
+
424
+ num = getmntinfo(buf, 2)
425
+
426
+ if num == 0
427
+ raise Error, 'getmntinfo() function failed: ' + strerror(FFI.errno)
428
+ end
429
+
430
+ ptr = buf.get_pointer(0)
431
+
432
+ num.times{ |i|
433
+ mnt = Statfs.new(ptr)
434
+ obj = Sys::Filesystem::Mount.new
435
+ obj.name = mnt[:f_mntfromname].to_s
436
+ obj.mount_point = mnt[:f_mntonname].to_s
437
+ obj.mount_type = mnt[:f_fstypename].to_s
438
+
439
+ string = ""
440
+ flags = mnt[:f_flags] & MNT_VISFLAGMASK
441
+
442
+ @@opt_names.each{ |key,val|
443
+ if flags & key > 0
444
+ if string.empty?
445
+ string << val
446
+ else
447
+ string << ", #{val}"
448
+ end
449
+ end
450
+ flags &= ~key
451
+ }
452
+
453
+ obj.options = string
454
+
455
+ if block_given?
456
+ yield obj.freeze
457
+ else
458
+ array << obj.freeze
459
+ end
460
+
461
+ ptr += Statfs.size
462
+ }
463
+ else
464
+ begin
465
+ if respond_to?(:setmntent, true)
466
+ fp = setmntent(MOUNT_FILE, 'r')
467
+ else
468
+ fp = fopen(MOUNT_FILE, 'r')
469
+ end
470
+
471
+ if RbConfig::CONFIG['host_os'] =~ /sunos|solaris/i
472
+ mt = Mnttab.new
473
+ while getmntent(fp, mt) == 0
474
+ obj = Sys::Filesystem::Mount.new
475
+ obj.name = mt[:mnt_special].to_s
476
+ obj.mount_point = mt[:mnt_mountp].to_s
477
+ obj.mount_type = mt[:mnt_fstype].to_s
478
+ obj.options = mt[:mnt_mntopts].to_s
479
+ obj.mount_time = Time.at(Integer(mt[:mnt_time]))
480
+
481
+ if block_given?
482
+ yield obj.freeze
483
+ else
484
+ array << obj.freeze
485
+ end
486
+ end
487
+ else
488
+ while ptr = getmntent(fp)
489
+ break if ptr.null?
490
+ mt = Mntent.new(ptr)
491
+
492
+ obj = Sys::Filesystem::Mount.new
493
+ obj.name = mt[:mnt_fsname]
494
+ obj.mount_point = mt[:mnt_dir]
495
+ obj.mount_type = mt[:mnt_type]
496
+ obj.options = mt[:mnt_opts]
497
+ obj.mount_time = nil
498
+ obj.dump_frequency = mt[:mnt_freq]
499
+ obj.pass_number = mt[:mnt_passno]
500
+
501
+ if block_given?
502
+ yield obj.freeze
503
+ else
504
+ array << obj.freeze
505
+ end
506
+ end
507
+ end
508
+ ensure
509
+ if fp && !fp.null?
510
+ if respond_to?(:endmntent, true)
511
+ endmntent(fp)
512
+ else
513
+ fclose(fp)
514
+ end
515
+ end
516
+ end
517
+ end
518
+
519
+ array
520
+ end
521
+
522
+ # Returns the mount point of the given +file+, or itself if it cannot
523
+ # be found.
524
+ #
525
+ # Example:
526
+ #
527
+ # Sys::Filesystem.mount_point('/home/some_user') # => /home
528
+ #
529
+ def self.mount_point(file)
530
+ dev = File.stat(file).dev
531
+ val = file
532
+
533
+ self.mounts.each{ |mnt|
534
+ mp = mnt.mount_point
535
+ if File.stat(mp).dev == dev
536
+ val = mp
537
+ break
538
+ end
539
+ }
540
+
541
+ val
542
+ end
543
+ end
544
+ end
545
+
546
+ class Fixnum
547
+ # Converts a number to kilobytes.
548
+ def to_kb
549
+ self / 1024
550
+ end
551
+
552
+ # Converts a number to megabytes.
553
+ def to_mb
554
+ self / 1048576
555
+ end
556
+
557
+ # Converts a number to gigabytes.
558
+ def to_gb
559
+ self / 1073741824
560
+ end
561
+ end