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 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