solaris-file 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES +19 -0
- data/MANIFEST +14 -0
- data/README +112 -0
- data/examples/test.rb +60 -0
- data/extconf.rb +8 -0
- data/lib/solaris/sfile.c +423 -0
- data/lib/solaris/sfile.h +78 -0
- data/test/tc_solaris_file.rb +156 -0
- metadata +53 -0
data/CHANGES
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
== 0.3.1 - 11-Jul-2006
|
2
|
+
* Fixed a potential 64 bit bug in a struct declaration.
|
3
|
+
* Minor RDoc updates, changes and internal cosmetic changes.
|
4
|
+
|
5
|
+
== 0.3.0 - 13-Jun-2005
|
6
|
+
* Added the File.resolvepath and File.realpath methods.
|
7
|
+
* Code cleanup. Attempting to pass a path greater than PATH_MAX
|
8
|
+
now raises an ArgumentError.
|
9
|
+
* More tests added.
|
10
|
+
|
11
|
+
== 0.2.0 - 1-Apr-2005
|
12
|
+
* The Solaris::FileError class has been changed to File::SolarisError.
|
13
|
+
* Added internal taint checking for methods that accept String arguments.
|
14
|
+
* Improved error handling and general cleanup.
|
15
|
+
* Added README and CHANGES files. The former replaces the doc/file.txt file.
|
16
|
+
* Added a gemspec.
|
17
|
+
|
18
|
+
== 0.1.0 - 27-Jan-2005
|
19
|
+
* Initial release.
|
data/MANIFEST
ADDED
data/README
ADDED
@@ -0,0 +1,112 @@
|
|
1
|
+
== Description
|
2
|
+
Adds ACL support for the File class on Solaris.
|
3
|
+
|
4
|
+
== Prerequisites
|
5
|
+
Ruby 1.8.x
|
6
|
+
|
7
|
+
== Installation
|
8
|
+
ruby extconf.rb
|
9
|
+
make
|
10
|
+
ruby test/tc_solaris_file.rb
|
11
|
+
make site-install
|
12
|
+
|
13
|
+
== Synopsis
|
14
|
+
require "solaris/file"
|
15
|
+
|
16
|
+
f = "some_file.txt"
|
17
|
+
acl_text = "user::rw-,user:nobody:r--,"
|
18
|
+
acl_text << "group::r--,group:sys:r--,mask:r--,other:r--"
|
19
|
+
|
20
|
+
File.trivial?(f) # probably true
|
21
|
+
File.acl_write_text(acl_text)
|
22
|
+
|
23
|
+
# No longer a trivial file
|
24
|
+
File.trivial?(f) # false
|
25
|
+
File.acl_read(f).each{ |acl| p acl }
|
26
|
+
|
27
|
+
== Class Methods
|
28
|
+
File.acl_count(file_name)
|
29
|
+
Returns the number of ACL entries for the file. Returns 0 if it's a
|
30
|
+
trivial file.
|
31
|
+
|
32
|
+
File.acl_read(file_name)
|
33
|
+
Returns an Array of ACLStruct's or nil if file_name is a trivial file.
|
34
|
+
|
35
|
+
Fiel.acl_read_text(file_name)
|
36
|
+
Returns a String form of the ACL entries for the file. Returns nil if
|
37
|
+
it's a trivial ACL.
|
38
|
+
|
39
|
+
File.acl_write_text(file_name, acl_string)
|
40
|
+
Accepts a formatted ACL string that set the ACL for the file. If the
|
41
|
+
string is badly formed, a File::SolarisError is raised.
|
42
|
+
|
43
|
+
File.realpath(path)
|
44
|
+
Resolves all symbolic links in +path+. Resolves to an absolute pathname
|
45
|
+
where possible.
|
46
|
+
|
47
|
+
File.resolvepath(path)
|
48
|
+
Resolves all symbolic links in +path+. All "." components are removed, as
|
49
|
+
well as all nonleading ".." components and their preceding directory
|
50
|
+
component. If leading ".." components resolve to the root directory, they
|
51
|
+
are replaced by "/".
|
52
|
+
|
53
|
+
File.trivial?(file_name)
|
54
|
+
Returns true if the file is a trivial ACL file, i.e. has no ACL entries for
|
55
|
+
additional users or groups.
|
56
|
+
|
57
|
+
= Instance Methods
|
58
|
+
File#acl_count
|
59
|
+
Returns the number of ACL entries for the file. Returns 0 if it's a
|
60
|
+
trivial file.
|
61
|
+
|
62
|
+
File#acl_read
|
63
|
+
Returns an Array of ACLStruct's.
|
64
|
+
|
65
|
+
File#acl_read_text
|
66
|
+
Returns a String form of the ACL entries for the file. Returns nil if
|
67
|
+
it's a trivial ACL.
|
68
|
+
|
69
|
+
File#acl_write_text(acl_string)
|
70
|
+
Accepts a formatted ACL string that set the ACL for the file. If the
|
71
|
+
string is badly formed, a File::SolarisError is raised.
|
72
|
+
|
73
|
+
File#trivial?
|
74
|
+
Returns true if the file is a trivial ACL file, i.e. has no ACL entries for
|
75
|
+
additional users or groups
|
76
|
+
|
77
|
+
== Constants
|
78
|
+
File::SOLARIS_VERSION
|
79
|
+
Returns the current version number of this package as a String.
|
80
|
+
|
81
|
+
== Error Classes
|
82
|
+
File::SolarisError < StandardError
|
83
|
+
Raised if anything goes wrong with any of the above methods.
|
84
|
+
|
85
|
+
== Known Bugs
|
86
|
+
None that I am aware of. Please report any bugs using the tracker on the
|
87
|
+
project page at http://www.rubyforge.org/projects/solarisutils
|
88
|
+
|
89
|
+
== Future Plans
|
90
|
+
Add File.acl_write and File#acl_write methods that accept an array of
|
91
|
+
ACLStruct's.
|
92
|
+
Add support for extended file attributes.
|
93
|
+
|
94
|
+
== Developer's Notes
|
95
|
+
This is a BETA release. I have not totally settled on the API.
|
96
|
+
|
97
|
+
== Copyright
|
98
|
+
(C) 2005, 2006 Daniel J. Berger
|
99
|
+
All Rights Reserved
|
100
|
+
|
101
|
+
== Warranty
|
102
|
+
This package is provided "as is" and without any express or
|
103
|
+
implied warranties, including, without limitation, the implied
|
104
|
+
warranties of merchantability and fitness for a particular purpose.
|
105
|
+
|
106
|
+
== License
|
107
|
+
Ruby's
|
108
|
+
|
109
|
+
== Author
|
110
|
+
Daniel J. Berger
|
111
|
+
djberg96 [at gmail dot com]
|
112
|
+
imperator on IRC (irc.freenode.net)
|
data/examples/test.rb
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
#############################################################
|
2
|
+
# test.rb
|
3
|
+
#
|
4
|
+
# Test script for general futzing. Modify as you see fit.
|
5
|
+
#############################################################
|
6
|
+
base = File.basename(Dir.pwd)
|
7
|
+
|
8
|
+
if base == "examples" || base =~ /solaris-file.*/
|
9
|
+
require "ftools"
|
10
|
+
Dir.chdir("..") if base == "examples"
|
11
|
+
Dir.mkdir("solaris") unless File.exists?("solaris")
|
12
|
+
File.copy("file.so","solaris")
|
13
|
+
$LOAD_PATH.unshift(Dir.pwd)
|
14
|
+
Dir.chdir("examples")
|
15
|
+
end
|
16
|
+
|
17
|
+
require "solaris/file"
|
18
|
+
require "pp"
|
19
|
+
|
20
|
+
puts
|
21
|
+
puts "Version: " + File::SOLARIS_VERSION
|
22
|
+
puts "-" * 20
|
23
|
+
|
24
|
+
file1 = "foo.txt"
|
25
|
+
file2 = "bar.txt"
|
26
|
+
|
27
|
+
File.open(file1,"w+"){ |fh| fh.puts "foo" }
|
28
|
+
File.open(file2,"w+"){ |fh| fh.puts "bar" }
|
29
|
+
|
30
|
+
acl_text = "user::rw-,user:nobody:r--,group::r--,group:sys:r--,mask:r--,other:r--"
|
31
|
+
acl_text2 = "user::rw-,user:nobody:r--,group::r--,group:sys:r--,mask:r--,other:rw-"
|
32
|
+
|
33
|
+
pp File.acl_count(file1) # Should be 0
|
34
|
+
pp File.acl_read(file1) # Should return nil
|
35
|
+
pp File.acl_read_text(file1) # Should return nil
|
36
|
+
pp File.trivial?(file1) # Should return true
|
37
|
+
|
38
|
+
File.acl_write_text(file1,acl_text)
|
39
|
+
|
40
|
+
pp File.acl_count(file1) # Should now be 6
|
41
|
+
pp File.acl_read(file1) # Should return 6 ACL Struct's
|
42
|
+
pp File.acl_read_text(file1) # Should return an string like 'acl_text' above
|
43
|
+
pp File.trivial?(file1) # Should return false
|
44
|
+
|
45
|
+
pp File.acl_count(file2)
|
46
|
+
pp File.acl_read(file2)
|
47
|
+
pp File.acl_read_text(file2)
|
48
|
+
pp File.trivial?(file2)
|
49
|
+
|
50
|
+
# Use instance methods instead this time
|
51
|
+
File.open(file2){ |fh|
|
52
|
+
fh.acl_write_text(acl_text2)
|
53
|
+
pp fh.acl_count
|
54
|
+
pp fh.acl_read
|
55
|
+
pp fh.acl_read_text
|
56
|
+
pp fh.trivial?
|
57
|
+
}
|
58
|
+
|
59
|
+
File.delete(file1) if File.exists?(file1)
|
60
|
+
File.delete(file2) if File.exists?(file2)
|
data/extconf.rb
ADDED
data/lib/solaris/sfile.c
ADDED
@@ -0,0 +1,423 @@
|
|
1
|
+
#include "ruby.h"
|
2
|
+
#include "rubyio.h"
|
3
|
+
#include <sys/acl.h>
|
4
|
+
#include <sys/param.h>
|
5
|
+
#include <unistd.h>
|
6
|
+
#include "sfile.h"
|
7
|
+
|
8
|
+
/*
|
9
|
+
* call-seq:
|
10
|
+
* File.acl_count(file_name)
|
11
|
+
*
|
12
|
+
* Returns the number of ACL entries for +file_name+. Returns 0 if +file_name+
|
13
|
+
* is a trivial file.
|
14
|
+
*/
|
15
|
+
static VALUE acl_count(VALUE klass, VALUE v_path){
|
16
|
+
int num_acls = 0;
|
17
|
+
char pathp[PATH_MAX];
|
18
|
+
|
19
|
+
SafeStringValue(v_path);
|
20
|
+
|
21
|
+
if(strlcpy(pathp, StringValuePtr(v_path), PATH_MAX) >= PATH_MAX)
|
22
|
+
rb_raise(rb_eArgError, "path length exceeds limit of: %i", PATH_MAX);
|
23
|
+
|
24
|
+
if((num_acls = acl(pathp, GETACLCNT, 0, NULL)) == -1)
|
25
|
+
rb_sys_fail(0);
|
26
|
+
|
27
|
+
if(num_acls == MIN_ACL_ENTRIES)
|
28
|
+
num_acls = 0;
|
29
|
+
|
30
|
+
return INT2FIX(num_acls);
|
31
|
+
}
|
32
|
+
|
33
|
+
/*
|
34
|
+
* call-seq:
|
35
|
+
* File#acl_count
|
36
|
+
*
|
37
|
+
* Returns the number of ACL entries for the current handle. Returns 0 if
|
38
|
+
* the file is a trivial file.
|
39
|
+
*/
|
40
|
+
static VALUE acl_icount(VALUE self){
|
41
|
+
int num_acls = 0;
|
42
|
+
int fildes = FIX2INT(rb_funcall(self,rb_intern("fileno"), 0, 0));
|
43
|
+
|
44
|
+
if((num_acls = facl(fildes, GETACLCNT, 0, NULL)) == -1)
|
45
|
+
rb_sys_fail(0);
|
46
|
+
|
47
|
+
if(num_acls == MIN_ACL_ENTRIES)
|
48
|
+
num_acls = 0;
|
49
|
+
|
50
|
+
return INT2FIX(num_acls);
|
51
|
+
}
|
52
|
+
|
53
|
+
/*
|
54
|
+
* call-seq:
|
55
|
+
* File.acl_read(file_name)
|
56
|
+
*
|
57
|
+
* Returns an array of ACLStruct's that contain three members each:
|
58
|
+
*
|
59
|
+
* - acl_type (String)
|
60
|
+
* - acl_id (Integer)
|
61
|
+
* - acl_perm (Integer)
|
62
|
+
*
|
63
|
+
* Returns nil if +file_name+ is a trivial file.
|
64
|
+
*/
|
65
|
+
static VALUE acl_read(VALUE klass, VALUE v_path){
|
66
|
+
int num_acls = 0;
|
67
|
+
char pathp[PATH_MAX];
|
68
|
+
VALUE v_array = Qnil;
|
69
|
+
int i;
|
70
|
+
|
71
|
+
SafeStringValue(v_path);
|
72
|
+
|
73
|
+
if(strlcpy(pathp, StringValuePtr(v_path), PATH_MAX) >= PATH_MAX)
|
74
|
+
rb_raise(rb_eArgError, "path length exceeds limit of: %i", PATH_MAX);
|
75
|
+
|
76
|
+
if((num_acls = acl(pathp, GETACLCNT, 0, NULL)) == -1)
|
77
|
+
rb_sys_fail(0);
|
78
|
+
|
79
|
+
if(num_acls != MIN_ACL_ENTRIES){
|
80
|
+
aclent_t* acl_buf;
|
81
|
+
v_array = rb_ary_new();
|
82
|
+
|
83
|
+
if( (acl_buf = malloc(sizeof(aclent_t) * num_acls) ) == NULL)
|
84
|
+
rb_sys_fail(0);
|
85
|
+
|
86
|
+
if(acl(pathp, GETACL, num_acls, acl_buf) != num_acls)
|
87
|
+
rb_sys_fail(0);
|
88
|
+
|
89
|
+
for(i = 0; i < num_acls; i++){
|
90
|
+
rb_ary_push(v_array,
|
91
|
+
rb_struct_new(sACLStruct,
|
92
|
+
acl_type_string(acl_buf[i].a_type),
|
93
|
+
INT2FIX(acl_buf[i].a_id),
|
94
|
+
INT2FIX(acl_buf[i].a_perm)
|
95
|
+
)
|
96
|
+
);
|
97
|
+
}
|
98
|
+
|
99
|
+
free(acl_buf);
|
100
|
+
}
|
101
|
+
|
102
|
+
return v_array;
|
103
|
+
}
|
104
|
+
|
105
|
+
/*
|
106
|
+
* call-seq:
|
107
|
+
* File#acl_read
|
108
|
+
*
|
109
|
+
* Returns an array of ACLStruct's that contain three members each:
|
110
|
+
*
|
111
|
+
* - acl_type (String)
|
112
|
+
* - acl_id (Integer)
|
113
|
+
* - acl_perm (Integer)
|
114
|
+
*
|
115
|
+
* Returns nil if the file is a trivial file.
|
116
|
+
*/
|
117
|
+
static VALUE acl_iread(VALUE self){
|
118
|
+
int num_acls = 0;
|
119
|
+
int fildes = FIX2INT(rb_funcall(self,rb_intern("fileno"),0,0));
|
120
|
+
VALUE v_array = Qnil;
|
121
|
+
int i;
|
122
|
+
|
123
|
+
if( (num_acls = facl(fildes, GETACLCNT, 0, NULL)) == -1)
|
124
|
+
rb_sys_fail(0);
|
125
|
+
|
126
|
+
if(num_acls != MIN_ACL_ENTRIES){
|
127
|
+
aclent_t* acl_buf;
|
128
|
+
v_array = rb_ary_new();
|
129
|
+
|
130
|
+
if( (acl_buf = malloc(sizeof(aclent_t) * num_acls) ) == NULL)
|
131
|
+
rb_sys_fail(0);
|
132
|
+
|
133
|
+
if(facl(fildes, GETACL, num_acls, acl_buf) != num_acls)
|
134
|
+
rb_sys_fail(0);
|
135
|
+
|
136
|
+
for(i = 0; i < num_acls; i++){
|
137
|
+
rb_ary_push(v_array,
|
138
|
+
rb_struct_new(sACLStruct,
|
139
|
+
acl_type_string(acl_buf[i].a_type),
|
140
|
+
INT2FIX(acl_buf[i].a_id),
|
141
|
+
INT2FIX(acl_buf[i].a_perm)
|
142
|
+
)
|
143
|
+
);
|
144
|
+
}
|
145
|
+
|
146
|
+
free(acl_buf);
|
147
|
+
}
|
148
|
+
return v_array;
|
149
|
+
}
|
150
|
+
|
151
|
+
/*
|
152
|
+
* call-seq:
|
153
|
+
* File.acl_read_text(file_name)
|
154
|
+
*
|
155
|
+
* Returns a textual representation of the ACL for +file_name+. If +file_name+
|
156
|
+
* is a trivial file, nil is returned.
|
157
|
+
*/
|
158
|
+
static VALUE acl_read_text(VALUE klass, VALUE v_path){
|
159
|
+
aclent_t* acl_buf;
|
160
|
+
int num_acls = 0;
|
161
|
+
char* acl_text;
|
162
|
+
char pathp[PATH_MAX];
|
163
|
+
VALUE v_text = Qnil;
|
164
|
+
|
165
|
+
SafeStringValue(v_path);
|
166
|
+
|
167
|
+
if(strlcpy(pathp, StringValuePtr(v_path), PATH_MAX) >= PATH_MAX)
|
168
|
+
rb_raise(rb_eArgError, "path length exceeds limit of: %i", PATH_MAX);
|
169
|
+
|
170
|
+
if( (num_acls = acl(pathp, GETACLCNT, 0, NULL)) == -1)
|
171
|
+
rb_sys_fail(0);
|
172
|
+
|
173
|
+
if(num_acls != MIN_ACL_ENTRIES){
|
174
|
+
if( (acl_buf = malloc(sizeof(aclent_t) * num_acls) ) == NULL)
|
175
|
+
rb_sys_fail(0);
|
176
|
+
|
177
|
+
if(acl(pathp, GETACL, num_acls, acl_buf) != num_acls)
|
178
|
+
rb_sys_fail(0);
|
179
|
+
|
180
|
+
acl_text = acltotext(acl_buf, num_acls);
|
181
|
+
|
182
|
+
free(acl_buf);
|
183
|
+
v_text = rb_str_new2(acl_text);
|
184
|
+
}
|
185
|
+
|
186
|
+
return v_text;
|
187
|
+
}
|
188
|
+
|
189
|
+
/*
|
190
|
+
* call-seq:
|
191
|
+
* File.acl_write_text(file_name, acl_text)
|
192
|
+
*
|
193
|
+
* Sets the ACL for +file_name+ using +acl_text+. The +acl_text+ argument is a
|
194
|
+
* human readable ACL text String.
|
195
|
+
*
|
196
|
+
* If +acl_text+ is invalid then a Solaris::FileError is raised, and in most
|
197
|
+
* cases the offending entry number will be identified.
|
198
|
+
*/
|
199
|
+
static VALUE acl_write_text(VALUE klass, VALUE v_path, VALUE v_text){
|
200
|
+
aclent_t* acl_buf;
|
201
|
+
int num_acls, which;
|
202
|
+
char pathp[PATH_MAX];
|
203
|
+
char* acl_text = StringValuePtr(v_text);
|
204
|
+
int rv;
|
205
|
+
|
206
|
+
SafeStringValue(v_path);
|
207
|
+
SafeStringValue(v_text);
|
208
|
+
|
209
|
+
if(strlcpy(pathp, StringValuePtr(v_path), PATH_MAX) >= PATH_MAX)
|
210
|
+
rb_raise(rb_eArgError, "path length exceeds limit of: %i", PATH_MAX);
|
211
|
+
|
212
|
+
if((acl_buf = aclfromtext(acl_text, &num_acls)) == NULL)
|
213
|
+
rb_raise(cSolarisFileError, "invalid ACL text");
|
214
|
+
|
215
|
+
rv = aclcheck(acl_buf, num_acls, &which);
|
216
|
+
do_acl_check(rv, which);
|
217
|
+
|
218
|
+
if(acl(pathp, SETACL, num_acls, acl_buf) == -1){
|
219
|
+
free(acl_text);
|
220
|
+
rb_sys_fail(0);
|
221
|
+
}
|
222
|
+
|
223
|
+
free(acl_text);
|
224
|
+
free(acl_buf);
|
225
|
+
|
226
|
+
return klass;
|
227
|
+
}
|
228
|
+
|
229
|
+
/*
|
230
|
+
* call-seq:
|
231
|
+
* File.resolvepath(path)
|
232
|
+
*
|
233
|
+
* Resolves all symbolic links in +path+. All "." components are removed, as
|
234
|
+
* well as all nonleading ".." components and their preceding directory
|
235
|
+
* component. If leading ".." components resolve to the root directory, they
|
236
|
+
* are replaced by "/".
|
237
|
+
*/
|
238
|
+
static VALUE solaris_resolvepath(VALUE klass, VALUE v_path){
|
239
|
+
char pathp[PATH_MAX];
|
240
|
+
|
241
|
+
SafeStringValue(v_path);
|
242
|
+
memset(pathp, 0, PATH_MAX);
|
243
|
+
|
244
|
+
if(resolvepath(StringValuePtr(v_path), pathp, PATH_MAX) == -1)
|
245
|
+
rb_sys_fail(0);
|
246
|
+
|
247
|
+
return rb_str_new2(pathp);
|
248
|
+
}
|
249
|
+
|
250
|
+
/*
|
251
|
+
* call-seq:
|
252
|
+
* File.realpath(path)
|
253
|
+
*
|
254
|
+
* Resolves all symbolic links in +path+. Resolves to an absolute pathname
|
255
|
+
* where possible.
|
256
|
+
*
|
257
|
+
* The difference between this method and File.resolvepath is that this method
|
258
|
+
* will resolve to an absolute pathname where possible.
|
259
|
+
*/
|
260
|
+
static VALUE solaris_realpath(VALUE klass, VALUE v_path){
|
261
|
+
char pathp[PATH_MAX];
|
262
|
+
|
263
|
+
SafeStringValue(v_path);
|
264
|
+
|
265
|
+
if(realpath(StringValuePtr(v_path), pathp) == NULL)
|
266
|
+
rb_sys_fail(0);
|
267
|
+
|
268
|
+
return rb_str_new2(pathp);
|
269
|
+
}
|
270
|
+
|
271
|
+
/* Instance Methods */
|
272
|
+
|
273
|
+
/*
|
274
|
+
* call-seq:
|
275
|
+
* File#acl_write_text(acl_text)
|
276
|
+
*
|
277
|
+
* Sets the ACL for the file using +acl_text+. The +acl_text+ argument is a
|
278
|
+
* human readable ACL text String.
|
279
|
+
*
|
280
|
+
* If +acl_text+ is invalid then a File::SolarisError is raised, and in most
|
281
|
+
* cases the offending entry number will be identified.
|
282
|
+
*/
|
283
|
+
static VALUE acl_iwrite_text(VALUE self, VALUE v_text){
|
284
|
+
aclent_t* acl_buf;
|
285
|
+
int num_acls, which;
|
286
|
+
char* acl_text = StringValuePtr(v_text);
|
287
|
+
int fildes = FIX2INT(rb_funcall(self, rb_intern("fileno"), 0, 0));
|
288
|
+
int rv;
|
289
|
+
|
290
|
+
SafeStringValue(v_text);
|
291
|
+
|
292
|
+
if((acl_buf = aclfromtext(acl_text, &num_acls)) == NULL)
|
293
|
+
rb_raise(cSolarisFileError, "invalid ACL text");
|
294
|
+
|
295
|
+
rv = aclcheck(acl_buf, num_acls, &which);
|
296
|
+
do_acl_check(rv, which);
|
297
|
+
|
298
|
+
if(facl(fildes, SETACL, num_acls, acl_buf) == -1){
|
299
|
+
free(acl_text);
|
300
|
+
rb_sys_fail(0);
|
301
|
+
}
|
302
|
+
|
303
|
+
free(acl_text);
|
304
|
+
free(acl_buf);
|
305
|
+
|
306
|
+
return self;
|
307
|
+
}
|
308
|
+
|
309
|
+
|
310
|
+
/*
|
311
|
+
* call-seq:
|
312
|
+
* File#acl_read_text
|
313
|
+
*
|
314
|
+
* Returns a textual representation of the ACL for the current handle. If
|
315
|
+
* the file is a trivial file, nil is returned.
|
316
|
+
*/
|
317
|
+
static VALUE acl_iread_text(VALUE self){
|
318
|
+
char* acl_text;
|
319
|
+
int num_acls = 0;
|
320
|
+
int fildes = FIX2INT(rb_funcall(self,rb_intern("fileno"),0,0));
|
321
|
+
VALUE v_text = Qnil;
|
322
|
+
|
323
|
+
if( (num_acls = facl(fildes,GETACLCNT,0,NULL)) == -1)
|
324
|
+
rb_sys_fail(0);
|
325
|
+
|
326
|
+
if(num_acls != MIN_ACL_ENTRIES){
|
327
|
+
aclent_t* acl_buf;
|
328
|
+
|
329
|
+
if( (acl_buf = malloc(sizeof(aclent_t) * num_acls) ) == NULL)
|
330
|
+
rb_sys_fail(0);
|
331
|
+
|
332
|
+
if(facl(fildes, GETACL, num_acls, acl_buf) != num_acls)
|
333
|
+
rb_sys_fail(0);
|
334
|
+
|
335
|
+
acl_text = acltotext(acl_buf,num_acls);
|
336
|
+
|
337
|
+
free(acl_buf);
|
338
|
+
v_text = rb_str_new2(acl_text);
|
339
|
+
}
|
340
|
+
|
341
|
+
return v_text;
|
342
|
+
}
|
343
|
+
|
344
|
+
/*
|
345
|
+
* call-seq:
|
346
|
+
* File.trivial?(file_name)
|
347
|
+
*
|
348
|
+
* Returns true if +file_name+ is a trivial file, i.e. has no additional ACL
|
349
|
+
* entries. Otherwise, it returns false.
|
350
|
+
*/
|
351
|
+
static VALUE acl_trivial(VALUE klass, VALUE v_path){
|
352
|
+
char pathp[PATH_MAX];
|
353
|
+
int num_acls = 0;
|
354
|
+
VALUE v_bool = Qfalse;
|
355
|
+
|
356
|
+
SafeStringValue(v_path);
|
357
|
+
|
358
|
+
if(strlcpy(pathp, StringValuePtr(v_path), PATH_MAX) >= PATH_MAX)
|
359
|
+
rb_raise(rb_eArgError, "path length exceeds limit of: %i", PATH_MAX);
|
360
|
+
|
361
|
+
if((num_acls = acl(pathp, GETACLCNT, 0, NULL)) == -1)
|
362
|
+
rb_sys_fail(0);
|
363
|
+
|
364
|
+
if(num_acls == MIN_ACL_ENTRIES)
|
365
|
+
v_bool = Qtrue;
|
366
|
+
|
367
|
+
return v_bool;
|
368
|
+
}
|
369
|
+
|
370
|
+
/*
|
371
|
+
* call-seq:
|
372
|
+
* File#trivial?
|
373
|
+
*
|
374
|
+
* Returns true if the current file is a trivial file, i.e. has no additional
|
375
|
+
* ACL entries. Otherwise, it returns false.
|
376
|
+
*/
|
377
|
+
static VALUE acl_itrivial(VALUE self){
|
378
|
+
int fildes = FIX2INT(rb_funcall(self,rb_intern("fileno"),0,0));
|
379
|
+
int num_acls = 0;
|
380
|
+
VALUE v_bool = Qfalse;
|
381
|
+
|
382
|
+
if( (num_acls = facl(fildes,GETACLCNT,0,NULL)) == -1)
|
383
|
+
rb_sys_fail(0);
|
384
|
+
|
385
|
+
if(num_acls == MIN_ACL_ENTRIES)
|
386
|
+
v_bool = Qtrue;
|
387
|
+
|
388
|
+
return v_bool;
|
389
|
+
}
|
390
|
+
|
391
|
+
/*
|
392
|
+
* Adds ACL support for the File class on Solaris
|
393
|
+
*/
|
394
|
+
void Init_file(){
|
395
|
+
/* Error Classes */
|
396
|
+
cSolarisFileError = rb_define_class_under(rb_cFile, "SolarisError",
|
397
|
+
rb_eStandardError
|
398
|
+
);
|
399
|
+
|
400
|
+
/* Class Methods */
|
401
|
+
rb_define_singleton_method(rb_cFile, "acl_count", acl_count, 1);
|
402
|
+
rb_define_singleton_method(rb_cFile, "acl_read", acl_read, 1);
|
403
|
+
rb_define_singleton_method(rb_cFile, "acl_read_text", acl_read_text, 1);
|
404
|
+
rb_define_singleton_method(rb_cFile, "acl_write_text", acl_write_text, 2);
|
405
|
+
rb_define_singleton_method(rb_cFile, "trivial?", acl_trivial, 1);
|
406
|
+
rb_define_singleton_method(rb_cFile, "realpath", solaris_realpath, 1);
|
407
|
+
rb_define_singleton_method(rb_cFile, "resolvepath", solaris_resolvepath, 1);
|
408
|
+
|
409
|
+
/* Instance Methods */
|
410
|
+
rb_define_method(rb_cFile,"acl_count", acl_icount, 0);
|
411
|
+
rb_define_method(rb_cFile,"acl_read", acl_iread, 0);
|
412
|
+
rb_define_method(rb_cFile,"acl_read_text", acl_iread_text, 0);
|
413
|
+
rb_define_method(rb_cFile,"acl_write_text", acl_iwrite_text, 1);
|
414
|
+
rb_define_method(rb_cFile,"trivial?", acl_itrivial, 0);
|
415
|
+
|
416
|
+
/* Structs */
|
417
|
+
sACLStruct = rb_struct_define("ACLStruct",
|
418
|
+
"acl_type", "acl_id", "acl_perm", NULL
|
419
|
+
);
|
420
|
+
|
421
|
+
/* Constants */
|
422
|
+
rb_define_const(rb_cFile, "SOLARIS_VERSION", rb_str_new2(SOLARIS_VERSION));
|
423
|
+
}
|
data/lib/solaris/sfile.h
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
#define SOLARIS_VERSION "0.3.1"
|
2
|
+
#define MAX_STRING 512
|
3
|
+
|
4
|
+
VALUE cSolarisFileError;
|
5
|
+
VALUE sACLStruct;
|
6
|
+
|
7
|
+
/*
|
8
|
+
* Converts a numeric ACL type into a human readable string.
|
9
|
+
*/
|
10
|
+
static VALUE acl_type_string(int acl_type){
|
11
|
+
VALUE rbACLType;
|
12
|
+
|
13
|
+
switch(acl_type){
|
14
|
+
case USER: case USER_OBJ:
|
15
|
+
rbACLType = rb_str_new2("user");
|
16
|
+
break;
|
17
|
+
case GROUP: case GROUP_OBJ:
|
18
|
+
rbACLType = rb_str_new2("group");
|
19
|
+
break;
|
20
|
+
case OTHER_OBJ:
|
21
|
+
rbACLType = rb_str_new2("other");
|
22
|
+
break;
|
23
|
+
case CLASS_OBJ:
|
24
|
+
rbACLType = rb_str_new2("mask");
|
25
|
+
break;
|
26
|
+
case DEF_USER: case DEF_USER_OBJ:
|
27
|
+
rbACLType = rb_str_new2("defaultuser");
|
28
|
+
break;
|
29
|
+
case DEF_GROUP: case DEF_GROUP_OBJ:
|
30
|
+
rbACLType = rb_str_new2("defaultgroup");
|
31
|
+
break;
|
32
|
+
case DEF_OTHER_OBJ:
|
33
|
+
rbACLType = rb_str_new2("defaultother");
|
34
|
+
break;
|
35
|
+
case DEF_CLASS_OBJ:
|
36
|
+
rbACLType = rb_str_new2("defaultmask");
|
37
|
+
break;
|
38
|
+
default:
|
39
|
+
rbACLType = rb_str_new2("unknown");
|
40
|
+
}
|
41
|
+
return rbACLType;
|
42
|
+
}
|
43
|
+
|
44
|
+
/*
|
45
|
+
* Helper function used by the acl_write_text class and instance methods.
|
46
|
+
*/
|
47
|
+
void do_acl_check(int aclcheck_val, int which){
|
48
|
+
char err[MAX_STRING];
|
49
|
+
|
50
|
+
switch(aclcheck_val){
|
51
|
+
case 0:
|
52
|
+
break; /* Nothing wrong */
|
53
|
+
case USER_ERROR:
|
54
|
+
sprintf(err,"Invalid ACL entry: %i; Multiple user entries", which);
|
55
|
+
rb_raise(cSolarisFileError,err);
|
56
|
+
case GRP_ERROR:
|
57
|
+
sprintf(err,"Invalid ACL entry: %i; Multiple group entries", which);
|
58
|
+
rb_raise(cSolarisFileError,err);
|
59
|
+
case OTHER_ERROR:
|
60
|
+
sprintf(err,"Invalid ACL entry: %i; Multiple other entries", which);
|
61
|
+
rb_raise(cSolarisFileError,err);
|
62
|
+
case CLASS_ERROR:
|
63
|
+
sprintf(err,"Invalid ACL entry: %i; Multiple mask entries", which);
|
64
|
+
rb_raise(cSolarisFileError,err);
|
65
|
+
case DUPLICATE_ERROR:
|
66
|
+
sprintf(err,"Invalid ACL entry: %i; Multiple user or group entries", which);
|
67
|
+
rb_raise(cSolarisFileError,err);
|
68
|
+
case ENTRY_ERROR:
|
69
|
+
sprintf(err,"Invalid ACL entry: %i; Invalid entry type", which);
|
70
|
+
rb_raise(cSolarisFileError,err);
|
71
|
+
case MISS_ERROR:
|
72
|
+
rb_raise(cSolarisFileError, "Missing ACL entries");
|
73
|
+
case MEM_ERROR:
|
74
|
+
rb_raise(cSolarisFileError, "Out of memory!");
|
75
|
+
default:
|
76
|
+
rb_raise(cSolarisFileError, "Unknown error");
|
77
|
+
};
|
78
|
+
}
|
@@ -0,0 +1,156 @@
|
|
1
|
+
###############################################################################
|
2
|
+
# tc_solaris_file.rb
|
3
|
+
#
|
4
|
+
# Test suite for the solaris-file package.
|
5
|
+
###############################################################################
|
6
|
+
base = File.basename(Dir.pwd)
|
7
|
+
|
8
|
+
if base == "test" || base =~ /solaris-file.*/
|
9
|
+
require "ftools"
|
10
|
+
Dir.chdir("..") if base == "test"
|
11
|
+
Dir.mkdir("solaris") unless File.exists?("solaris")
|
12
|
+
File.copy("file.so","solaris")
|
13
|
+
$LOAD_PATH.unshift(Dir.pwd)
|
14
|
+
Dir.chdir("test") rescue nil
|
15
|
+
end
|
16
|
+
|
17
|
+
require "test/unit"
|
18
|
+
require "solaris/file"
|
19
|
+
require "open3"
|
20
|
+
|
21
|
+
class TC_Solaris_File < Test::Unit::TestCase
|
22
|
+
def setup
|
23
|
+
@dir = Dir.pwd
|
24
|
+
@file = "foo.txt" # trivial
|
25
|
+
@file2 = "bar.txt" # non-trivial
|
26
|
+
|
27
|
+
@acl_text = "user::rw-,user:nobody:r--,"
|
28
|
+
@acl_text << "group::r--,group:sys:r--,mask:r--,other:r--"
|
29
|
+
|
30
|
+
File.open(@file,"w+"){ |fh| fh.puts "foo" } unless File.exists?(@file)
|
31
|
+
File.open(@file2,"w+"){ |fh| fh.puts "bar" } unless File.exists?(@file2)
|
32
|
+
|
33
|
+
system("setfacl -m #{@acl_text} #{@file2}") # Make @file2 non-trivial
|
34
|
+
|
35
|
+
@fh = File.open(@file)
|
36
|
+
@fh2 = File.open(@file2)
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_version
|
40
|
+
assert_equal("0.3.1", File::SOLARIS_VERSION)
|
41
|
+
end
|
42
|
+
|
43
|
+
# CLASS METHODS
|
44
|
+
|
45
|
+
# This yields an ACL struct for each entry
|
46
|
+
def test_class_acl_read
|
47
|
+
assert_respond_to(File,:acl_read)
|
48
|
+
assert_nothing_raised{ File.acl_read(@file) }
|
49
|
+
assert_equal(nil,File.acl_read(@file))
|
50
|
+
assert_kind_of(Array,File.acl_read(@file2))
|
51
|
+
assert_raises(Errno::ENOENT){ File.acl_read("bogus") }
|
52
|
+
assert_raises(ArgumentError){ File.acl_read("bogus"*500) }
|
53
|
+
end
|
54
|
+
|
55
|
+
# Tests the acltotext() approach
|
56
|
+
def test_class_acl_read_text
|
57
|
+
assert_respond_to(File,:acl_read_text)
|
58
|
+
assert_nothing_raised{ File.acl_read_text(@file) }
|
59
|
+
assert_kind_of(NilClass,File.acl_read_text(@file))
|
60
|
+
assert_kind_of(String,File.acl_read_text(@file2))
|
61
|
+
assert_raises(Errno::ENOENT){ File.acl_read_text("bogus") }
|
62
|
+
end
|
63
|
+
|
64
|
+
# Tests the aclfromtext() approach
|
65
|
+
def test_class_acl_write_text
|
66
|
+
text = "user::rw-,group::r--,mask:r--,other:---"
|
67
|
+
assert_respond_to(File,:acl_write_text)
|
68
|
+
assert_nothing_raised{ File.acl_write_text(@file,text) }
|
69
|
+
assert_raises(File::SolarisError){ File.acl_write_text(@file,"bogus") }
|
70
|
+
end
|
71
|
+
|
72
|
+
def test_class_acl_trivial
|
73
|
+
assert_respond_to(File,:trivial?)
|
74
|
+
assert_nothing_raised{ File.trivial?(@file) }
|
75
|
+
assert_equal(true,File.trivial?(@file))
|
76
|
+
assert_equal(false,File.trivial?(@file2))
|
77
|
+
assert_raises(Errno::ENOENT){ File.trivial?("bogus") }
|
78
|
+
assert_raises(ArgumentError){ File.trivial?("bogus"*500) }
|
79
|
+
end
|
80
|
+
|
81
|
+
def test_class_acl_count
|
82
|
+
assert_respond_to(File, :acl_count)
|
83
|
+
assert_nothing_raised{ File.acl_count(@file) }
|
84
|
+
assert_kind_of(Fixnum, File.acl_count(@file))
|
85
|
+
assert_raises(Errno::ENOENT){ File.acl_count("bogus") }
|
86
|
+
assert_raises(ArgumentError){ File.acl_count("bogus"*500) }
|
87
|
+
end
|
88
|
+
|
89
|
+
def test_class_realpath
|
90
|
+
dir1 = File.dirname(Dir.pwd) + "/examples"
|
91
|
+
dir2 = Dir.pwd + "/.././examples"
|
92
|
+
|
93
|
+
assert_respond_to(File, :realpath)
|
94
|
+
assert_nothing_raised{ File.realpath(@dir) }
|
95
|
+
assert_equal(@dir, File.realpath(@dir))
|
96
|
+
assert_equal(dir1, File.realpath(dir2))
|
97
|
+
assert_raises(Errno::ENOENT){ File.realpath("bogus") }
|
98
|
+
end
|
99
|
+
|
100
|
+
def test_class_resolvepath
|
101
|
+
assert_respond_to(File, :resolvepath)
|
102
|
+
assert_nothing_raised{ File.resolvepath(@dir) }
|
103
|
+
assert_equal(@dir, File.resolvepath(@dir))
|
104
|
+
assert_equal("../examples", File.resolvepath("../examples"))
|
105
|
+
assert_raises(Errno::ENOENT){ File.resolvepath("bogus") }
|
106
|
+
end
|
107
|
+
|
108
|
+
# INSTANCE METHODS
|
109
|
+
|
110
|
+
def test_instance_acl
|
111
|
+
assert_respond_to(@fh, :acl_read)
|
112
|
+
assert_nothing_raised{ @fh.acl_read }
|
113
|
+
assert_equal(nil, @fh.acl_read)
|
114
|
+
assert_kind_of(Array, @fh2.acl_read)
|
115
|
+
assert_kind_of(Struct::ACLStruct, @fh2.acl_read.first)
|
116
|
+
end
|
117
|
+
|
118
|
+
# Tests the acltotext() approach
|
119
|
+
def test_instance_acl_read_text
|
120
|
+
assert_respond_to(@fh,:acl_read_text)
|
121
|
+
assert_nothing_raised{ @fh.acl_read_text }
|
122
|
+
assert_equal(nil, @fh.acl_read_text)
|
123
|
+
assert_kind_of(String, @fh2.acl_read_text)
|
124
|
+
end
|
125
|
+
|
126
|
+
# Tests the aclfromtext() approach
|
127
|
+
def test_instance_acl_write_text
|
128
|
+
text = "user::rw-,group::r--,mask:r--,other:---"
|
129
|
+
assert_respond_to(@fh2,:acl_write_text)
|
130
|
+
assert_nothing_raised{ @fh2.acl_write_text(text) }
|
131
|
+
assert_raises(File::SolarisError){ @fh2.acl_write_text("bogus") }
|
132
|
+
end
|
133
|
+
|
134
|
+
def test_instance_acl_trivial
|
135
|
+
assert_respond_to(@fh, :trivial?)
|
136
|
+
assert_nothing_raised{ @fh.trivial? }
|
137
|
+
assert_equal(true, @fh.trivial?)
|
138
|
+
assert_equal(false, @fh2.trivial?)
|
139
|
+
end
|
140
|
+
|
141
|
+
def test_instance_acl_count
|
142
|
+
assert_respond_to(@fh,:acl_count)
|
143
|
+
assert_nothing_raised{ @fh.acl_count }
|
144
|
+
assert_kind_of(Fixnum,@fh.acl_count)
|
145
|
+
assert_equal(0,@fh.acl_count)
|
146
|
+
assert_equal(6,@fh2.acl_count)
|
147
|
+
end
|
148
|
+
|
149
|
+
def teardown
|
150
|
+
@dir = nil
|
151
|
+
@file = nil
|
152
|
+
@file2 = nil
|
153
|
+
@fh.close
|
154
|
+
@fh2.close
|
155
|
+
end
|
156
|
+
end
|
metadata
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
rubygems_version: 0.9.0
|
3
|
+
specification_version: 1
|
4
|
+
name: solaris-file
|
5
|
+
version: !ruby/object:Gem::Version
|
6
|
+
version: 0.3.1
|
7
|
+
date: 2006-07-11 00:00:00 -06:00
|
8
|
+
summary: ACL and other methods for the File class on Solaris
|
9
|
+
require_paths:
|
10
|
+
- lib
|
11
|
+
email: djberg96@gmail.com
|
12
|
+
homepage: http://www.rubyforge.org/projects/sysutils
|
13
|
+
rubyforge_project:
|
14
|
+
description: ACL and other methods for the File class on Solaris
|
15
|
+
autorequire:
|
16
|
+
default_executable:
|
17
|
+
bindir: bin
|
18
|
+
has_rdoc: true
|
19
|
+
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">"
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.0.0
|
24
|
+
version:
|
25
|
+
platform: ruby
|
26
|
+
signing_key:
|
27
|
+
cert_chain:
|
28
|
+
post_install_message:
|
29
|
+
authors:
|
30
|
+
- Daniel Berger
|
31
|
+
files:
|
32
|
+
- CHANGES
|
33
|
+
- MANIFEST
|
34
|
+
- README
|
35
|
+
- extconf.rb
|
36
|
+
- lib/solaris/sfile.c
|
37
|
+
- lib/solaris/sfile.h
|
38
|
+
- examples/test.rb
|
39
|
+
test_files:
|
40
|
+
- test/tc_solaris_file.rb
|
41
|
+
rdoc_options: []
|
42
|
+
|
43
|
+
extra_rdoc_files:
|
44
|
+
- CHANGES
|
45
|
+
- README
|
46
|
+
executables: []
|
47
|
+
|
48
|
+
extensions:
|
49
|
+
- extconf.rb
|
50
|
+
requirements: []
|
51
|
+
|
52
|
+
dependencies: []
|
53
|
+
|