sys-filesystem 0.2.0 → 0.3.0
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.
- data/CHANGES +6 -0
- data/README +13 -4
- data/Rakefile +4 -0
- data/ext/extconf.rb +5 -0
- data/ext/sys/filesystem.c +165 -9
- data/test/test_sys_filesystem_unix.rb +19 -5
- data/test/test_sys_filesystem_windows.rb +7 -1
- metadata +2 -2
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
|
-
|
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
|
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'
|
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-
|
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
|
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
|
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
|
-
|
152
|
-
|
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
|
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.
|
294
|
-
rb_define_const(cFilesys, "VERSION", rb_str_new2("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.
|
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, "
|
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(
|
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
|
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
|
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.
|
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.
|
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:
|
12
|
+
date: 2009-01-26 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|