sys-filesystem 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGES CHANGED
@@ -1,3 +1,9 @@
1
+ == 0.3.0 - 26-Feb-2009
2
+ * Added support for OS X and FreeBSD thanks to an awesome patch by Nobuyoshi
3
+ Miyokawa.
4
+ * Added the Filesystem.mount_point method that takes a file and returns
5
+ the mount point its sitting on.
6
+
1
7
  == 0.2.0 - 30-Dec-2008
2
8
  * Added the Filesystem.mounts method for iterating over mount or volume
3
9
  information.
data/README CHANGED
@@ -13,7 +13,8 @@
13
13
  require 'sys/filesystem'
14
14
  include Sys
15
15
 
16
- p Filesystem.stat("/")
16
+ # Display information about a particular filesystem.
17
+ p Filesystem.stat('/')
17
18
 
18
19
  # Sample output
19
20
 
@@ -33,11 +34,15 @@
33
34
  @blocks_free = 20301129
34
35
  >
35
36
 
37
+ # Describe all mount points on the system
36
38
  Filesystem.mounts{ |mount| p mount }
39
+
40
+ # Find the mount point of any particular file
41
+ puts Filesystem.mount_point('/home/djberge/some_file.txt') => '/home'
37
42
 
38
43
  = Notes
39
44
  === MS Windows
40
- This is a pure Ruby implementation using the windows-pr package, which in
45
+ This is a pure Ruby implementation using the windows-pr library, which in
41
46
  turn wraps native Windows functions.
42
47
  === UNIX
43
48
  This is a C extension that wraps statvfs, etc.
@@ -54,11 +59,15 @@
54
59
  Suggestions welcome.
55
60
 
56
61
  = Acknowledgements
57
- Mike Hall, for ideas and code that I borrowed from his 'filesystem' package.
62
+ Mike Hall, for ideas and code that I borrowed from his 'filesystem'
63
+ library.
64
+
58
65
  Park Heesob, for implementation and API ideas for the MS Windows version.
66
+
67
+ Nobuyoshi Miyokawa, for adding FreeBSD and OS X support.
59
68
 
60
69
  = Copyright
61
- (C) 2003-2008 Daniel J. Berger
70
+ (C) 2003-2009 Daniel J. Berger
62
71
  All Rights Reserved
63
72
 
64
73
  = Warranty
data/Rakefile CHANGED
@@ -58,6 +58,10 @@ Rake::TestTask.new("test") do |t|
58
58
  t.test_files = FileList['test/test_sys_filesystem.rb']
59
59
  end
60
60
 
61
+ task :test do
62
+ Rake.application[:clean].execute
63
+ end
64
+
61
65
  desc "Run the example program"
62
66
  task :example => [:build] do |t|
63
67
  Dir.chdir('examples') do
data/ext/extconf.rb CHANGED
@@ -7,6 +7,11 @@ have_header("sys/mnttab.h") || have_header("mntent.h")
7
7
  have_func("statvfs")
8
8
  have_func("getextmntent")
9
9
 
10
+ if have_func("getmntinfo")
11
+ have_macro("MNT_NOATIME", "sys/mount.h")
12
+ have_macro("MNT_MULTILABEL", "sys/mount.h")
13
+ end
14
+
10
15
  if have_header("sys/statvfs.h")
11
16
  have_struct_member("struct statvfs", "f_basetype", "sys/statvfs.h")
12
17
  else
data/ext/sys/filesystem.c CHANGED
@@ -19,17 +19,35 @@
19
19
  #ifdef HAVE_SYS_MNTTAB_H /* Solaris */
20
20
 
21
21
  #include <sys/mnttab.h>
22
- #define MNTENT mnttab
22
+ #define MNTENT mnttab
23
23
  #define START_MNT(F,M) fopen(F,M)
24
24
  #define GET_MNT(FP,MP) (getmntent(FP,MP) == 0)
25
25
  #define END_MNT(F) fclose(F)
26
26
  #define MOUNTFILE "/etc/mnttab"
27
27
 
28
+ #elif HAVE_GETMNTINFO
29
+
30
+ struct _ment {
31
+ struct statfs *mntbufp;
32
+ int current;
33
+ int max;
34
+ };
35
+
36
+ #include <sys/param.h>
37
+ #include <sys/mount.h>
38
+ #include <sys/vnode.h>
39
+
40
+ #define MNTENT _ment
41
+ #define START_MNT(F,M) start_mnt(F,M)
42
+ #define GET_MNT(FP,MP) ((MP = get_mnt(MP)) != NULL)
43
+ #define END_MNT(F) end_mnt(F)
44
+ #define MOUNTFILE "getmntinfo"
45
+
28
46
  #else /* Most flavors of UNIX */
29
47
 
30
48
  #ifdef HAVE_MNTENT_H
31
49
  #include <mntent.h>
32
- #define MNTENT mntent
50
+ #define MNTENT mntent
33
51
  #define START_MNT(F,M) setmntent(F,M)
34
52
  #define GET_MNT(FP,MP) ((MP = getmntent(FP)) != NULL)
35
53
  #define END_MNT(F) endmntent(F)
@@ -38,6 +56,71 @@
38
56
 
39
57
  #endif
40
58
 
59
+ #ifdef HAVE_GETMNTINFO
60
+
61
+ /* below table comes from FreeBSD mount.c@1.105 */
62
+ static struct opt {
63
+ int o_opt;
64
+ const char *o_name;
65
+ } optnames[] = {
66
+ { MNT_ASYNC, "asynchronous" },
67
+ { MNT_EXPORTED, "NFS exported" },
68
+ { MNT_LOCAL, "local" },
69
+ { MNT_NOEXEC, "noexec" },
70
+ { MNT_NOSUID, "nosuid" },
71
+ { MNT_QUOTA, "with quotas" },
72
+ { MNT_RDONLY, "read-only" },
73
+ { MNT_SYNCHRONOUS, "synchronous" },
74
+ { MNT_UNION, "union" },
75
+ #ifdef HAVE_MNT_MULTILABEL
76
+ { MNT_MULTILABEL, "multilabel" },
77
+ #endif
78
+ #ifdef HAVE_MNT_NOATIME
79
+ { MNT_NOATIME, "noatime" },
80
+ #endif
81
+ #if !defined(__MACH__) || !defined(__APPLE__)
82
+ { MNT_NOSYMFOLLOW, "nosymfollow" },
83
+ { MNT_NOCLUSTERR, "noclusterr" },
84
+ { MNT_NOCLUSTERW, "noclusterw" },
85
+ { MNT_SUIDDIR, "suiddir" },
86
+ { MNT_SOFTDEP, "soft-updates" },
87
+ { MNT_ACLS, "acls" },
88
+ #endif
89
+ { 0, NULL }
90
+ };
91
+
92
+ static FILE* start_mnt(const char *filename, const char *type)
93
+ {
94
+ return (FILE*)!0; /* do nothing */
95
+ }
96
+
97
+ static struct _ment* get_mnt(struct _ment* m)
98
+ {
99
+ struct _ment* ret = m;
100
+
101
+ if (m->max == 0) {
102
+ if ((m->max = getmntinfo(&(m->mntbufp), MNT_NOWAIT)) == 0) {
103
+ return NULL; /* XXX */
104
+ }
105
+ m->current = 0;
106
+ }
107
+
108
+ if (m->current >= m->max) {
109
+ ret = NULL;
110
+ }
111
+ m->current++;
112
+
113
+ return ret;
114
+ }
115
+
116
+ static void end_mnt(FILE* fp)
117
+ {
118
+ /* do nothing */
119
+ }
120
+
121
+ #endif
122
+
123
+
41
124
  VALUE mSys, cFilesys, cStat, cMount;
42
125
 
43
126
  static VALUE create_mount_object(struct MNTENT*);
@@ -63,7 +146,7 @@ static VALUE fs_stat(VALUE klass, VALUE v_path){
63
146
  struct proc p;
64
147
 
65
148
  if(VFS_STATFS(&mp, &fs, &p) < 0)
66
- rb_sys_fail("VFS_STATFS");
149
+ rb_sys_fail("VFS_STATFS");
67
150
  #endif
68
151
 
69
152
  v_stat = rb_funcall(cStat, rb_intern("new"), 0, 0);
@@ -148,8 +231,12 @@ static VALUE fs_mounts(VALUE klass){
148
231
  FILE* fp;
149
232
  struct MNTENT* mp;
150
233
  #ifdef HAVE_SYS_MNTTAB_H
151
- struct MNTENT mt;
152
- mp = &mt;
234
+ struct MNTENT mt;
235
+ mp = &mt;
236
+ #elif HAVE_GETMNTINFO
237
+ struct MNTENT mt;
238
+ mt.max = 0;
239
+ mp = &mt;
153
240
  #endif
154
241
 
155
242
  v_array = Qnil;
@@ -168,7 +255,7 @@ static VALUE fs_mounts(VALUE klass){
168
255
  }
169
256
 
170
257
  END_MNT(fp);
171
-
258
+
172
259
  return v_array; /* nil in block form */
173
260
  }
174
261
 
@@ -184,6 +271,32 @@ static VALUE create_mount_object(struct MNTENT* mp){
184
271
  rb_iv_set(v_mount, "@mount_time", rb_time_new(atoi(mp->mnt_time), 0));
185
272
  rb_iv_set(v_mount, "@dump_frequency", Qnil);
186
273
  rb_iv_set(v_mount, "@pass_number", Qnil);
274
+ #elif HAVE_GETMNTINFO
275
+ {
276
+ struct statfs *p = mp->mntbufp + (mp->current-1);
277
+ struct opt *o;
278
+ int flags, mul;
279
+ char ostr[BUFSIZ];
280
+
281
+ flags = p->f_flags & MNT_VISFLAGMASK;
282
+ ostr[0] = '\0';
283
+
284
+ for (mul = 0, o = optnames; flags && o->o_opt; o++) {
285
+ if (flags & o->o_opt) {
286
+ strlcat(ostr, ((mul++) ? "," : ""), BUFSIZ);
287
+ strlcat(ostr, o->o_name, BUFSIZ);
288
+ flags &= ~o->o_opt;
289
+ }
290
+ }
291
+
292
+ rb_iv_set(v_mount, "@name", rb_tainted_str_new2(p->f_mntfromname));
293
+ rb_iv_set(v_mount, "@mount_point", rb_tainted_str_new2(p->f_mntonname));
294
+ rb_iv_set(v_mount, "@mount_type", rb_tainted_str_new2(p->f_fstypename));
295
+ rb_iv_set(v_mount, "@options", rb_tainted_str_new2(ostr));
296
+ rb_iv_set(v_mount, "@mount_time", Qnil);
297
+ rb_iv_set(v_mount, "@dump_frequency", Qnil);
298
+ rb_iv_set(v_mount, "@pass_number", Qnil);
299
+ }
187
300
  #else
188
301
  rb_iv_set(v_mount, "@name", rb_tainted_str_new2(mp->mnt_fsname));
189
302
  rb_iv_set(v_mount, "@mount_point", rb_tainted_str_new2(mp->mnt_dir));
@@ -197,6 +310,48 @@ static VALUE create_mount_object(struct MNTENT* mp){
197
310
  return v_mount;
198
311
  }
199
312
 
313
+ /*
314
+ * call-seq:
315
+ * Filesystem.mount_point(file)
316
+ *
317
+ * Returns the mount point of the given +file+, or itself if it cannot be
318
+ * determined.
319
+ *
320
+ * Example:
321
+ *
322
+ * Filesystem.mount_point('/home/djberge/some_file.txt') => '/home'
323
+ */
324
+ static VALUE fs_mount_point(VALUE klass, VALUE v_file){
325
+ VALUE v_stat, v_stat_m, v_mounts, v_mount_pt, v_mount;
326
+ VALUE v_found = Qfalse;
327
+ long dev1, dev2;
328
+ int i = 0;
329
+
330
+ v_stat = rb_funcall(rb_cFile, rb_intern("stat"), 1, v_file);
331
+ v_mounts = fs_mounts(klass);
332
+ dev1 = FIX2LONG(rb_funcall(v_stat, rb_intern("dev"), 0, 0));
333
+
334
+ /* Stat each mount point and compare its device number with the device
335
+ * number of the file provided. If they match, we have a winner.
336
+ */
337
+ for(i = 0; i < RARRAY(v_mounts)->len; i++){
338
+ v_mount = RARRAY(v_mounts)->ptr[i];
339
+ v_mount_pt = rb_funcall(v_mount, rb_intern("mount_point"), 0, 0);
340
+ v_stat_m = rb_funcall(rb_cFile, rb_intern("stat"), 1, v_mount_pt);
341
+ dev2 = FIX2LONG(rb_funcall(v_stat_m, rb_intern("dev"), 0, 0));
342
+
343
+ if(dev1 == dev2){
344
+ v_found = Qtrue;
345
+ break;
346
+ }
347
+ }
348
+
349
+ if(v_found == Qtrue)
350
+ return v_mount_pt;
351
+ else
352
+ return v_file;
353
+ }
354
+
200
355
  void Init_filesystem(){
201
356
  /* The toplevel namespace */
202
357
  mSys = rb_define_module("Sys");
@@ -210,9 +365,10 @@ void Init_filesystem(){
210
365
  cMount = rb_define_class_under(cFilesys, "Mount", rb_cObject);
211
366
 
212
367
  /* Instances of this class are returned by the Filesystem.stat method */
213
- cStat = rb_define_class_under(cFilesys, "Stat", rb_cObject);
368
+ cStat = rb_define_class_under(cFilesys, "Stat", rb_cObject);
214
369
 
215
370
  /* Singleton methods */
371
+ rb_define_singleton_method(cFilesys, "mount_point", fs_mount_point, 1);
216
372
  rb_define_singleton_method(cFilesys, "mounts", fs_mounts, 0);
217
373
  rb_define_singleton_method(cFilesys, "stat", fs_stat, 1);
218
374
 
@@ -290,8 +446,8 @@ void Init_filesystem(){
290
446
 
291
447
  /* Constants */
292
448
 
293
- /* 0.2.0: The version of this library (a String) */
294
- rb_define_const(cFilesys, "VERSION", rb_str_new2("0.2.0"));
449
+ /* 0.3.0: The version of this library (a String) */
450
+ rb_define_const(cFilesys, "VERSION", rb_str_new2("0.3.0"));
295
451
 
296
452
  /* 0x00000001: Read only file system */
297
453
  rb_define_const(cStat, "RDONLY", INT2FIX(ST_RDONLY));
@@ -15,6 +15,8 @@ class TC_Sys_Filesystem_Unix < Test::Unit::TestCase
15
15
  def self.startup
16
16
  @@solaris = Config::CONFIG['host_os'] =~ /solaris/i
17
17
  @@linux = Config::CONFIG['host_os'] =~ /linux/i
18
+ @@freebsd = Config::CONFIG['host_os'] =~ /freebsd/i
19
+ @@darwin = Config::CONFIG['host_os'] =~ /darwin/i
18
20
  end
19
21
 
20
22
  def setup
@@ -26,7 +28,7 @@ class TC_Sys_Filesystem_Unix < Test::Unit::TestCase
26
28
  end
27
29
 
28
30
  def test_version
29
- assert_equal('0.2.0', Filesystem::VERSION)
31
+ assert_equal('0.3.0', Filesystem::VERSION)
30
32
  end
31
33
 
32
34
  def test_stat_path
@@ -105,7 +107,8 @@ class TC_Sys_Filesystem_Unix < Test::Unit::TestCase
105
107
  end
106
108
 
107
109
  def test_stat_base_type
108
- omit_unless(@@solaris, "NOTRUNC test skipped except on Solaris")
110
+ omit_unless(@@solaris, "base_type test skipped except on Solaris")
111
+
109
112
  assert_respond_to(@stat, :base_type)
110
113
  assert_kind_of(String, @stat.base_type)
111
114
  end
@@ -113,7 +116,9 @@ class TC_Sys_Filesystem_Unix < Test::Unit::TestCase
113
116
  def test_stat_constants
114
117
  assert_not_nil(Filesystem::Stat::RDONLY)
115
118
  assert_not_nil(Filesystem::Stat::NOSUID)
119
+
116
120
  omit_unless(@@solaris, "NOTRUNC test skipped except on Solaris")
121
+
117
122
  assert_not_nil(Filesystem::Stat::NOTRUNC)
118
123
  end
119
124
 
@@ -192,15 +197,16 @@ class TC_Sys_Filesystem_Unix < Test::Unit::TestCase
192
197
 
193
198
  def test_mount_time
194
199
  assert_respond_to(@mnt, :mount_time)
200
+
195
201
  if @@solaris
196
- assert_kind_of(String, @mnt.mount_time)
202
+ assert_kind_of(Time, @mnt.mount_time)
197
203
  else
198
204
  assert_nil(@mnt.mount_time)
199
205
  end
200
206
  end
201
207
 
202
208
  def test_mount_dump_frequency
203
- omit_if(@@solaris, 'dump_frequency test skipped on Solaris')
209
+ omit_if(@@solaris || @@freebsd || @@darwin, 'dump_frequency test skipped on this platform')
204
210
  assert_respond_to(@mnt, :dump_frequency)
205
211
  assert_kind_of(Fixnum, @mnt.dump_frequency)
206
212
  end
@@ -211,7 +217,7 @@ class TC_Sys_Filesystem_Unix < Test::Unit::TestCase
211
217
  end
212
218
 
213
219
  def test_mount_pass_number
214
- omit_if(@@solaris, 'pass_number test skipped on Solaris')
220
+ omit_if(@@solaris || @@freebsd || @@darwin, 'pass_number test skipped on this platform')
215
221
  assert_respond_to(@mnt, :pass_number)
216
222
  assert_kind_of(Fixnum, @mnt.pass_number)
217
223
  end
@@ -221,6 +227,12 @@ class TC_Sys_Filesystem_Unix < Test::Unit::TestCase
221
227
  assert_true(@mnt.method(:passno) == @mnt.method(:pass_number))
222
228
  end
223
229
 
230
+ def test_mount_point_singleton
231
+ assert_respond_to(Filesystem, :mount_point)
232
+ assert_nothing_raised{ Filesystem.mount_point(Dir.pwd) }
233
+ assert_kind_of(String, Filesystem.mount_point(Dir.pwd))
234
+ end
235
+
224
236
  def teardown
225
237
  @dir = nil
226
238
  @stat = nil
@@ -230,5 +242,7 @@ class TC_Sys_Filesystem_Unix < Test::Unit::TestCase
230
242
  def self.shutdown
231
243
  @@solaris = nil
232
244
  @@linux = nil
245
+ @@freebsd = nil
246
+ @@darwin = nil
233
247
  end
234
248
  end
@@ -22,7 +22,7 @@ class TC_Sys_Filesystem_Windows < Test::Unit::TestCase
22
22
  end
23
23
 
24
24
  def test_version
25
- assert_equal('0.2.0', Filesystem::VERSION)
25
+ assert_equal('0.3.0', Filesystem::VERSION)
26
26
  end
27
27
 
28
28
  def test_stat_path
@@ -93,6 +93,12 @@ class TC_Sys_Filesystem_Windows < Test::Unit::TestCase
93
93
  assert_kind_of(String, @stat.base_type)
94
94
  end
95
95
 
96
+ def test_mount_point_singleton
97
+ assert_respond_to(Filesystem, :mount_point)
98
+ assert_nothing_raised{ Filesystem.mount_point(Dir.pwd) }
99
+ assert_kind_of(String, Filesystem.mount_point(Dir.pwd))
100
+ end
101
+
96
102
  def test_constants
97
103
  assert_not_nil(Filesystem::CASE_SENSITIVE_SEARCH)
98
104
  assert_not_nil(Filesystem::CASE_PRESERVED_NAMES)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sys-filesystem
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel J. Berger
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2008-12-13 00:00:00 -07:00
12
+ date: 2009-01-26 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies: []
15
15