solaris-file 0.3.1
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 +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
|
+
|