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 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 Bugs
25
- None that I am aware of. Please report any bugs using the tracker on the
26
- project page at http://www.rubyforge.org/projects/solarisutils
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-2011 Daniel J. Berger
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 => [:build] do
43
- ruby "-Iext examples/example_solaris_file.rb"
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
@@ -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