solaris-file 0.3.7 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES +6 -0
- data/MANIFEST +5 -2
- data/README +9 -4
- data/Rakefile +3 -25
- data/lib/solaris/file.rb +370 -0
- data/lib/solaris/file/constants.rb +31 -0
- data/lib/solaris/file/functions.rb +19 -0
- data/lib/solaris/file/stat.rb +16 -0
- data/lib/solaris/file/structs.rb +11 -0
- data/solaris-file.gemspec +17 -20
- data/test/test_solaris_file.rb +115 -49
- metadata +58 -70
- data/ext/extconf.rb +0 -9
- data/ext/solaris/sfile.c +0 -498
- data/ext/solaris/sfile.h +0 -87
data/CHANGES
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
== 0.4.0 - 13-Jan-2013
|
2
|
+
* Converted to FFI.
|
3
|
+
* Removed the File.realpath method since it is now already defined in
|
4
|
+
Ruby 1.9 and later.
|
5
|
+
* Updated the tests and Rakefile.
|
6
|
+
|
1
7
|
== 0.3.7 - 29-Jul-2011
|
2
8
|
* Removed an obsolete gemspec attribute.
|
3
9
|
* Fixed redefinition warnings for the ftype and realpath singleton methods.
|
data/MANIFEST
CHANGED
@@ -3,7 +3,10 @@
|
|
3
3
|
* README
|
4
4
|
* Rakefile
|
5
5
|
* solaris-file.gemspec
|
6
|
+
* lib/solaris/file.rb
|
7
|
+
* lib/solaris/file/constants.rb
|
8
|
+
* lib/solaris/file/functions.rb
|
9
|
+
* lib/solaris/file/stat.rb
|
10
|
+
* lib/solaris/file/structs.rb
|
6
11
|
* examples/example_solaris_file.rb
|
7
|
-
* ext/solaris/sfile.c
|
8
|
-
* ext/solaris/sfile.h
|
9
12
|
* test/test_solaris_file.rb
|
data/README
CHANGED
@@ -21,16 +21,21 @@
|
|
21
21
|
File.door?("/var/run/syslog_door") # => true
|
22
22
|
File.ftype("/var/run/syslog_door") # => 'door'
|
23
23
|
|
24
|
-
== Known
|
25
|
-
|
26
|
-
|
24
|
+
== Known Issues
|
25
|
+
Although this libary uses FFI, the instance methods will probably not work
|
26
|
+
when using JRuby because of the underlying use of file descriptors. However,
|
27
|
+
the singleton methods should work.
|
28
|
+
|
29
|
+
Please report any other issues on the home page at:
|
30
|
+
|
31
|
+
http://www.github.com/djberg96/solaris-file
|
27
32
|
|
28
33
|
== Future Plans
|
29
34
|
Add acl_write methods that accept an array of ACLStruct's.
|
30
35
|
Add support for extended file attributes.
|
31
36
|
|
32
37
|
== Copyright
|
33
|
-
(C) 2005-
|
38
|
+
(C) 2005-2013 Daniel J. Berger
|
34
39
|
All Rights Reserved
|
35
40
|
|
36
41
|
== Warranty
|
data/Rakefile
CHANGED
@@ -1,28 +1,8 @@
|
|
1
1
|
require 'rake'
|
2
2
|
require 'rake/clean'
|
3
3
|
require 'rake/testtask'
|
4
|
-
require 'rbconfig'
|
5
|
-
include Config
|
6
4
|
|
7
|
-
CLEAN.include(
|
8
|
-
'**/*.gem', # Gem files
|
9
|
-
'**/*.rbc', # Rubinius
|
10
|
-
'**/*.o', # C object file
|
11
|
-
'**/*.log', # Ruby extension build log
|
12
|
-
'**/Makefile', # C Makefile
|
13
|
-
'**/conftest.dSYM', # OS X build directory
|
14
|
-
"**/*.#{CONFIG['DLEXT']}" # C shared object
|
15
|
-
)
|
16
|
-
|
17
|
-
desc "Build the solaris-file package (but don't install it)"
|
18
|
-
task :build => [:clean] do
|
19
|
-
Dir.chdir('ext') do
|
20
|
-
ruby 'extconf.rb'
|
21
|
-
sh 'make'
|
22
|
-
Dir.mkdir('solaris') unless File.exists?('solaris')
|
23
|
-
FileUtils.cp('file.so', 'solaris')
|
24
|
-
end
|
25
|
-
end
|
5
|
+
CLEAN.include('**/*.gem', '**/*.rbc')
|
26
6
|
|
27
7
|
namespace :gem do
|
28
8
|
desc "Create the solaris-file gem"
|
@@ -39,13 +19,11 @@ namespace :gem do
|
|
39
19
|
end
|
40
20
|
|
41
21
|
desc "Run the example program"
|
42
|
-
task :example
|
43
|
-
ruby "-
|
22
|
+
task :example do
|
23
|
+
ruby "-Ilib examples/example_solaris_file.rb"
|
44
24
|
end
|
45
25
|
|
46
26
|
Rake::TestTask.new do |t|
|
47
|
-
task :test => :build
|
48
|
-
t.libs << 'ext'
|
49
27
|
t.warning = true
|
50
28
|
t.verbose = true
|
51
29
|
end
|
data/lib/solaris/file.rb
ADDED
@@ -0,0 +1,370 @@
|
|
1
|
+
require 'ffi'
|
2
|
+
require File.join(File.dirname(__FILE__), 'file', 'stat')
|
3
|
+
require File.join(File.dirname(__FILE__), 'file', 'constants')
|
4
|
+
require File.join(File.dirname(__FILE__), 'file', 'structs')
|
5
|
+
require File.join(File.dirname(__FILE__), 'file', 'functions')
|
6
|
+
|
7
|
+
class File
|
8
|
+
include Solaris::Constants
|
9
|
+
include Solaris::Functions
|
10
|
+
include Solaris::Structs
|
11
|
+
extend Solaris::Functions
|
12
|
+
|
13
|
+
# The version of the solaris-file library
|
14
|
+
SOLARIS_VERSION = '0.4.0'
|
15
|
+
|
16
|
+
# We redefine the ftype method later.
|
17
|
+
class << self
|
18
|
+
alias ftype_orig ftype
|
19
|
+
remove_method :ftype
|
20
|
+
end
|
21
|
+
|
22
|
+
# Reads ACL information for the given file. Returns an array of ACLStruct's
|
23
|
+
# that contain three members each:
|
24
|
+
#
|
25
|
+
# - acl_type (String)
|
26
|
+
# - acl_id (Integer)
|
27
|
+
# - acl_perm (Integer)
|
28
|
+
#
|
29
|
+
# Example:
|
30
|
+
#
|
31
|
+
# File.acl_read(file)
|
32
|
+
#
|
33
|
+
# Returns nil if the file is a trivial file.
|
34
|
+
#
|
35
|
+
def self.acl_read(file)
|
36
|
+
num = acl(file, GETACLCNT, 0, nil)
|
37
|
+
|
38
|
+
if num < 0
|
39
|
+
raise SystemCallError.new('acl', FFI.errno)
|
40
|
+
end
|
41
|
+
|
42
|
+
arr = nil
|
43
|
+
|
44
|
+
if num != MIN_ACL_ENTRIES
|
45
|
+
ptr = FFI::MemoryPointer.new(AclEnt.new, num)
|
46
|
+
|
47
|
+
if acl(file, GETACL, num, ptr) < 0
|
48
|
+
raise SystemCallError.new('acl', FFI.errno)
|
49
|
+
end
|
50
|
+
|
51
|
+
arr = []
|
52
|
+
|
53
|
+
num.times{ |i|
|
54
|
+
ent = AclEnt.new(ptr[i])
|
55
|
+
arr << ACLStruct.new(
|
56
|
+
acl_type_string(ent[:a_type]), ent[:a_id], ent[:a_perm]
|
57
|
+
)
|
58
|
+
}
|
59
|
+
end
|
60
|
+
|
61
|
+
arr
|
62
|
+
end
|
63
|
+
|
64
|
+
# Reads ACL information for the current file. Returns an array of ACLStruct's
|
65
|
+
# that contain three members each:
|
66
|
+
#
|
67
|
+
# - acl_type (String)
|
68
|
+
# - acl_id (Integer)
|
69
|
+
# - acl_perm (Integer)
|
70
|
+
#
|
71
|
+
# Example:
|
72
|
+
#
|
73
|
+
# file#acl_read
|
74
|
+
#
|
75
|
+
# Returns nil if the file is a trivial file.
|
76
|
+
#
|
77
|
+
def acl_read
|
78
|
+
num = facl(fileno, GETACLCNT, 0, nil)
|
79
|
+
|
80
|
+
if num < 0
|
81
|
+
raise SystemCallError.new('facl', FFI.errno)
|
82
|
+
end
|
83
|
+
|
84
|
+
arr = nil
|
85
|
+
|
86
|
+
if num != MIN_ACL_ENTRIES
|
87
|
+
ptr = FFI::MemoryPointer.new(AclEnt.new, num)
|
88
|
+
|
89
|
+
if facl(fileno, GETACL, num, ptr) < 0
|
90
|
+
raise SystemCallError.new('facl', FFI.errno)
|
91
|
+
end
|
92
|
+
|
93
|
+
arr = []
|
94
|
+
|
95
|
+
num.times{ |i|
|
96
|
+
ent = AclEnt.new(ptr[i])
|
97
|
+
arr << ACLStruct.new(
|
98
|
+
self.class.acl_type_string(ent[:a_type]), ent[:a_id], ent[:a_perm]
|
99
|
+
)
|
100
|
+
}
|
101
|
+
end
|
102
|
+
|
103
|
+
arr
|
104
|
+
end
|
105
|
+
|
106
|
+
# Returns a textual representation of the ACL for the given file.
|
107
|
+
# If the file is a trivial file, nil is returned instead.
|
108
|
+
#
|
109
|
+
# Example:
|
110
|
+
#
|
111
|
+
# File.acl_read_text(file)
|
112
|
+
#
|
113
|
+
# Sample output:
|
114
|
+
#
|
115
|
+
# 'user::rw-,user:nobody:r--,group::r--,group:sys:r--,mask:r--,other:r--'
|
116
|
+
#
|
117
|
+
def self.acl_read_text(file)
|
118
|
+
num = acl(file, GETACLCNT, 0, nil)
|
119
|
+
|
120
|
+
if num < 0
|
121
|
+
raise SystemCallError.new('acl', FFI.errno)
|
122
|
+
end
|
123
|
+
|
124
|
+
text = nil
|
125
|
+
|
126
|
+
if num != MIN_ACL_ENTRIES
|
127
|
+
ptr = FFI::MemoryPointer.new(AclEnt.new, num)
|
128
|
+
|
129
|
+
if acl(file, GETACL, num, ptr) < 0
|
130
|
+
raise SystemCallError.new('acl', FFI.errno)
|
131
|
+
end
|
132
|
+
|
133
|
+
text = acltotext(ptr, num)
|
134
|
+
end
|
135
|
+
|
136
|
+
text
|
137
|
+
end
|
138
|
+
|
139
|
+
# Returns a textual representation of the ACL for the current file.
|
140
|
+
# If the file is a trivial file, nil is returned instead.
|
141
|
+
#
|
142
|
+
# Example:
|
143
|
+
#
|
144
|
+
# file#acl_read_text
|
145
|
+
#
|
146
|
+
# Sample output:
|
147
|
+
#
|
148
|
+
# 'user::rw-,user:nobody:r--,group::r--,group:sys:r--,mask:r--,other:r--'
|
149
|
+
#
|
150
|
+
def acl_read_text
|
151
|
+
num = facl(fileno, GETACLCNT, 0, nil)
|
152
|
+
|
153
|
+
if num < 0
|
154
|
+
raise SystemCallError.new('facl', FFI.errno)
|
155
|
+
end
|
156
|
+
|
157
|
+
text = nil
|
158
|
+
|
159
|
+
if num != MIN_ACL_ENTRIES
|
160
|
+
ptr = FFI::MemoryPointer.new(AclEnt.new, num)
|
161
|
+
|
162
|
+
if facl(fileno, GETACL, num, ptr) < 0
|
163
|
+
raise SystemCallError.new('acl', FFI.errno)
|
164
|
+
end
|
165
|
+
|
166
|
+
text = acltotext(ptr, num)
|
167
|
+
end
|
168
|
+
|
169
|
+
text
|
170
|
+
end
|
171
|
+
|
172
|
+
# Sets the ACL for the given file using +text+. The +text+ argument is a
|
173
|
+
# human readable ACL text string.
|
174
|
+
#
|
175
|
+
# If the text is invalid then a ArgumentError is raised, and in most
|
176
|
+
# cases the offending entry number will be identified.
|
177
|
+
#
|
178
|
+
# Example:
|
179
|
+
#
|
180
|
+
# File.acl_write_text(file, text)
|
181
|
+
#
|
182
|
+
def self.acl_write_text(file, text)
|
183
|
+
pnum = FFI::MemoryPointer.new(:int)
|
184
|
+
pwhich = FFI::MemoryPointer.new(:int)
|
185
|
+
|
186
|
+
ptr = aclfromtext(text, pnum)
|
187
|
+
|
188
|
+
if ptr.null?
|
189
|
+
raise ArgumentError, "invalid ACL text"
|
190
|
+
end
|
191
|
+
|
192
|
+
num = pnum.read_int
|
193
|
+
|
194
|
+
val = aclcheck(ptr, num, pwhich)
|
195
|
+
|
196
|
+
if val != 0
|
197
|
+
raise ArgumentError, aclcheck_string(val, pwhich.read_int)
|
198
|
+
end
|
199
|
+
|
200
|
+
if acl(file, SETACL, num, ptr) < 0
|
201
|
+
raise SystemCallError.new('acl', FFI.errno)
|
202
|
+
end
|
203
|
+
|
204
|
+
text
|
205
|
+
end
|
206
|
+
|
207
|
+
# Sets the ACL for the current file using +text+. The +text+ argument is a
|
208
|
+
# human readable ACL text string.
|
209
|
+
#
|
210
|
+
# If the text is invalid then a ArgumentError is raised, and in most
|
211
|
+
# cases the offending entry number will be identified.
|
212
|
+
#
|
213
|
+
# Example:
|
214
|
+
#
|
215
|
+
# file#acl_write_text(text)
|
216
|
+
#
|
217
|
+
def acl_write_text(text)
|
218
|
+
pnum = FFI::MemoryPointer.new(:int)
|
219
|
+
pwhich = FFI::MemoryPointer.new(:int)
|
220
|
+
|
221
|
+
ptr = aclfromtext(text, pnum)
|
222
|
+
|
223
|
+
if ptr.null?
|
224
|
+
raise ArgumentError, "invalid ACL text"
|
225
|
+
end
|
226
|
+
|
227
|
+
num = pnum.read_int
|
228
|
+
|
229
|
+
val = aclcheck(ptr, num, pwhich)
|
230
|
+
|
231
|
+
if val != 0
|
232
|
+
raise ArgumentError, aclcheck_string(val, pwhich.read_int)
|
233
|
+
end
|
234
|
+
|
235
|
+
if facl(fileno, SETACL, num, ptr) < 0
|
236
|
+
raise SystemCallError.new('facl', FFI.errno)
|
237
|
+
end
|
238
|
+
|
239
|
+
text
|
240
|
+
end
|
241
|
+
|
242
|
+
# Returns true if the given file is a trivial file, i.e. has no additional ACL
|
243
|
+
# entries. Otherwise, it returns false.
|
244
|
+
#
|
245
|
+
def self.trivial?(file)
|
246
|
+
num = acl(file, GETACLCNT, 0, nil)
|
247
|
+
|
248
|
+
raise SystemCallError.new('acl', FFI.errno) if num < 0
|
249
|
+
|
250
|
+
num == MIN_ACL_ENTRIES
|
251
|
+
end
|
252
|
+
|
253
|
+
# Returns true if the current file is a trivial file, i.e. has no additional ACL
|
254
|
+
# entries. Otherwise, it returns false.
|
255
|
+
#
|
256
|
+
def trivial?
|
257
|
+
num = facl(fileno, GETACLCNT, 0, nil)
|
258
|
+
|
259
|
+
raise SystemCallError.new('facl', FFI.errno) if num < 0
|
260
|
+
|
261
|
+
num == MIN_ACL_ENTRIES
|
262
|
+
end
|
263
|
+
|
264
|
+
# Returns the number of ACL entries for the given file. Returns 0 if the file
|
265
|
+
# is a trivial file.
|
266
|
+
#
|
267
|
+
def self.acl_count(file)
|
268
|
+
num = acl(file, GETACLCNT, 0, nil)
|
269
|
+
|
270
|
+
raise SystemCallError.new('acl', FFI.errno) if num < 0
|
271
|
+
|
272
|
+
num == MIN_ACL_ENTRIES ? 0 : num
|
273
|
+
end
|
274
|
+
|
275
|
+
# Returns the number of ACL entries for the current file. Returns 0 if the file
|
276
|
+
# is a trivial file.
|
277
|
+
#
|
278
|
+
def acl_count
|
279
|
+
num = facl(fileno, GETACLCNT, 0, nil)
|
280
|
+
|
281
|
+
raise SystemCallError.new('facl', FFI.errno) if num < 0
|
282
|
+
|
283
|
+
num == MIN_ACL_ENTRIES ? 0 : num
|
284
|
+
end
|
285
|
+
|
286
|
+
# Resolves all symbolic links in the given path. All "." components are
|
287
|
+
# removed, as well as all nonleading ".." components and their preceding
|
288
|
+
# directory component.
|
289
|
+
#
|
290
|
+
# If leading ".." components resolve to the root directory, they are
|
291
|
+
# replaced by "/".
|
292
|
+
#
|
293
|
+
def self.resolvepath(file)
|
294
|
+
ptr = FFI::MemoryPointer.new(:char, 1024)
|
295
|
+
|
296
|
+
if resolvepath_c(file, ptr, ptr.size) < 0
|
297
|
+
raise SystemCallError.new('resolvepath', FFI.errno)
|
298
|
+
end
|
299
|
+
|
300
|
+
ptr.read_string
|
301
|
+
end
|
302
|
+
|
303
|
+
# Returns true if the given file is door file, false otherwise.
|
304
|
+
#--
|
305
|
+
# Door files are special files used for interprocess communication between
|
306
|
+
# a client and server.
|
307
|
+
#
|
308
|
+
def self.door?(file)
|
309
|
+
File.stat(file).door?
|
310
|
+
end
|
311
|
+
|
312
|
+
# The same as the default ftype method, except that "door" is returned
|
313
|
+
# if the file is a door file.
|
314
|
+
#
|
315
|
+
def self.ftype(file)
|
316
|
+
File.stat(file).ftype
|
317
|
+
end
|
318
|
+
|
319
|
+
private
|
320
|
+
|
321
|
+
# Convert a numeric error code into a human readable string.
|
322
|
+
def self.aclcheck_string(val, int)
|
323
|
+
base_string = "Invalid entry: #{int} - "
|
324
|
+
|
325
|
+
case val
|
326
|
+
when USER_ERROR
|
327
|
+
base_string + "Multiple user entries"
|
328
|
+
when GRP_ERROR
|
329
|
+
base_string + "Multiple group entries"
|
330
|
+
when OTHER_ERROR
|
331
|
+
base_string + "Multiple other entries"
|
332
|
+
when CLASS_ERROR
|
333
|
+
base_string + "Multiple mask entries"
|
334
|
+
when DUPLICATE_ERROR
|
335
|
+
base_string + "Multiple user or group entries"
|
336
|
+
when ENTRY_ERROR
|
337
|
+
base_string + "Invalid entry type"
|
338
|
+
when MISS_ERROR
|
339
|
+
"Missing ACL entries"
|
340
|
+
when MEM_ERROR
|
341
|
+
"Out of memory"
|
342
|
+
else
|
343
|
+
"Unknown error"
|
344
|
+
end
|
345
|
+
end
|
346
|
+
|
347
|
+
# Convert a numeric acl type to a human readable string.
|
348
|
+
def self.acl_type_string(num)
|
349
|
+
case num
|
350
|
+
when USER, USER_OBJ
|
351
|
+
"user"
|
352
|
+
when GROUP, GROUP_OBJ
|
353
|
+
"group"
|
354
|
+
when OTHER_OBJ
|
355
|
+
"other"
|
356
|
+
when CLASS_OBJ
|
357
|
+
"mask"
|
358
|
+
when DEF_USER, DEF_USER_OBJ
|
359
|
+
"defaultuser"
|
360
|
+
when DEF_GROUP, DEF_GROUP_OBJ
|
361
|
+
"defaultgroup"
|
362
|
+
when DEF_OTHER_OBJ
|
363
|
+
"defaultother"
|
364
|
+
when DEF_CLASS_OBJ
|
365
|
+
"defaultmask"
|
366
|
+
else
|
367
|
+
"unknown"
|
368
|
+
end
|
369
|
+
end
|
370
|
+
end
|