solaris-file 0.3.7 → 0.4.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/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
|