ffi-xattr 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -2,3 +2,4 @@
2
2
  .bundle
3
3
  Gemfile.lock
4
4
  pkg/*
5
+ /doc/
data/.travis.yml ADDED
@@ -0,0 +1,6 @@
1
+ rvm:
2
+ - 1.8.7
3
+ - 1.9.2
4
+ - 1.9.3
5
+ before_script:
6
+ - sudo mount / -o remount,user_xattr
data/README.md CHANGED
@@ -3,23 +3,21 @@ ffi-xattr
3
3
 
4
4
  Ruby library to manage extended file attributes.
5
5
 
6
+ [![Build Status](https://secure.travis-ci.org/jarib/ffi-xattr.png)](http://travis-ci.org/jarib/ffi-xattr)
7
+
8
+
6
9
  Example
7
10
  -------
8
11
 
9
12
  xattr = Xattr.new("/path/to/file")
10
13
  xattr['user.foo'] = 'bar'
11
-
14
+
12
15
  xattr['user.foo'] #=> 'bar'
13
16
  xattr.list #=> ["user.foo"]
14
-
17
+
15
18
  xattr.each { |key, value| ... }
16
19
  xattr.as_json #=> {"user.foo" => "bar"}
17
20
 
18
- TODO
19
- ----
20
-
21
- * Handle symlinks
22
-
23
21
 
24
22
  Note on Patches/Pull Requests
25
23
  -----------------------------
data/Rakefile CHANGED
@@ -1,3 +1,5 @@
1
+ include Rake::DSL
2
+
1
3
  require 'bundler'
2
4
  Bundler::GemHelper.install_tasks
3
5
 
data/ffi-xattr.gemspec CHANGED
@@ -14,8 +14,10 @@ Gem::Specification.new do |s|
14
14
 
15
15
  s.rubyforge_project = "ffi-xattr"
16
16
 
17
- s.add_development_dependency "rspec", "~> 2.5"
17
+
18
18
  s.add_dependency "ffi"
19
+ s.add_development_dependency "rspec", "~> 2.5"
20
+ s.add_development_dependency "rake", "~> 0.9.2"
19
21
 
20
22
  s.files = `git ls-files`.split("\n")
21
23
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
@@ -1,4 +1,4 @@
1
- class Xattr
1
+ class Xattr # :nodoc: all
2
2
  module Lib
3
3
  extend FFI::Library
4
4
 
@@ -9,32 +9,39 @@ class Xattr
9
9
  attach_function :setxattr, [:string, :string, :pointer, :size_t, :uint, :int], :int
10
10
  attach_function :removexattr, [:string, :string, :int], :int
11
11
 
12
+ XATTR_NOFOLLOW = 0x0001
12
13
 
13
- def self.list(path)
14
- size = listxattr(path, nil, 0, 0)
15
- res_ptr = FFI::MemoryPointer.new(:pointer, size)
16
- listxattr(path, res_ptr, size, 0)
14
+ class << self
15
+ def list(path, no_follow)
16
+ options = no_follow ? XATTR_NOFOLLOW : 0
17
+ size = listxattr(path, nil, 0, options)
18
+ res_ptr = FFI::MemoryPointer.new(:pointer, size)
19
+ listxattr(path, res_ptr, size, options)
17
20
 
18
- res_ptr.read_string(size).split("\000")
19
- end
21
+ res_ptr.read_string(size).split("\000")
22
+ end
20
23
 
21
- def self.get(path, key)
22
- size = getxattr(path, key, nil, 0, 0, 0)
23
- return unless size > 0
24
+ def get(path, no_follow, key)
25
+ options = no_follow ? XATTR_NOFOLLOW : 0
26
+ size = getxattr(path, key, nil, 0, 0, options)
27
+ return unless size > 0
24
28
 
25
- str_ptr = FFI::MemoryPointer.new(:char, size);
26
- getxattr(path, key, str_ptr, size, 0, 0)
29
+ str_ptr = FFI::MemoryPointer.new(:char, size)
30
+ getxattr(path, key, str_ptr, size, 0, options)
27
31
 
28
- str_ptr.read_string
29
- end
32
+ str_ptr.read_string
33
+ end
30
34
 
31
- def self.set(path, key, value)
32
- Error.check setxattr(path, key, value, value.bytesize, 0, 0)
33
- end
35
+ def set(path, no_follow, key, value)
36
+ options = no_follow ? XATTR_NOFOLLOW : 0
37
+ Error.check setxattr(path, key, value, value.bytesize, 0, options)
38
+ end
34
39
 
35
- def self.remove(path, key)
36
- Error.check removexattr(path, key, 0)
40
+ def remove(path, no_follow, key)
41
+ options = no_follow ? XATTR_NOFOLLOW : 0
42
+ Error.check removexattr(path, key, options)
43
+ end
37
44
  end
38
45
 
39
46
  end
40
- end
47
+ end
@@ -1,4 +1,4 @@
1
- class Xattr
1
+ class Xattr # :nodoc: all
2
2
  module Error
3
3
  extend FFI::Library
4
4
 
@@ -6,15 +6,17 @@ class Xattr
6
6
 
7
7
  attach_function :strerror_r, [:int, :pointer, :size_t], :int
8
8
 
9
- def self.last
10
- ptr = FFI::MemoryPointer.new(:char, 256)
11
- strerrror_r(FFI.errno, ptr, 256)
9
+ class << self
10
+ def last
11
+ ptr = FFI::MemoryPointer.new(:char, 256)
12
+ strerror_r(FFI.errno, ptr, 256)
12
13
 
13
- ptr.read_string
14
- end
14
+ ptr.read_string
15
+ end
15
16
 
16
- def self.check(int)
17
- raise last if int != 0
17
+ def check(int)
18
+ raise last if int != 0
19
+ end
18
20
  end
19
21
  end
20
- end
22
+ end
@@ -1,4 +1,4 @@
1
- class Xattr
1
+ class Xattr # :nodoc: all
2
2
  module Lib
3
3
  extend FFI::Library
4
4
 
@@ -11,31 +11,42 @@ class Xattr
11
11
  attach_function :getxattr, [:string, :string, :pointer, :size_t], :int
12
12
  attach_function :removexattr, [:string, :string], :int
13
13
 
14
- def self.list(path)
15
- size = listxattr(path, nil, 0)
16
- res_ptr = FFI::MemoryPointer.new(:pointer, size)
17
- listxattr(path, res_ptr, size)
18
-
19
- res_ptr.read_string(size).split("\000")
20
- end
21
-
22
- def self.get(path, key)
23
- size = getxattr(path, key, nil, 0)
24
- return unless size > 0
25
-
26
- str_ptr = FFI::MemoryPointer.new(:char, size);
27
- getxattr(path, key, str_ptr, size)
28
-
29
- str_ptr.read_string
30
- end
31
-
32
- def self.set(path, key, value)
33
- Error.check setxattr(path, key, value, value.bytesize, 0)
34
- end
35
-
36
- def self.remove(path, key)
37
- Error.check removexattr(path, key.to_s)
14
+ attach_function :llistxattr, [:string, :pointer, :size_t], :size_t
15
+ attach_function :lsetxattr, [:string, :string, :pointer, :size_t, :int], :int
16
+ attach_function :lgetxattr, [:string, :string, :pointer, :size_t], :int
17
+ attach_function :lremovexattr, [:string, :string], :int
18
+
19
+ class << self
20
+ def list(path, no_follow)
21
+ method = no_follow ? :llistxattr : :listxattr
22
+ size = send(method, path, nil, 0)
23
+ res_ptr = FFI::MemoryPointer.new(:pointer, size)
24
+ send(method, path, res_ptr, size)
25
+
26
+ res_ptr.read_string(size).split("\000")
27
+ end
28
+
29
+ def get(path, no_follow, key)
30
+ method = no_follow ? :lgetxattr : :getxattr
31
+ size = send(method, path, key, nil, 0)
32
+ return unless size > 0
33
+
34
+ str_ptr = FFI::MemoryPointer.new(:char, size)
35
+ send(method, path, key, str_ptr, size)
36
+
37
+ str_ptr.read_string
38
+ end
39
+
40
+ def set(path, no_follow, key, value)
41
+ method = no_follow ? :lsetxattr : :setxattr
42
+ Error.check send(method, path, key, value, value.bytesize, 0)
43
+ end
44
+
45
+ def remove(path, no_follow, key)
46
+ method = no_follow ? :lremovexattr : :removexattr
47
+ Error.check send(method, path, key)
48
+ end
38
49
  end
39
50
 
40
51
  end
41
- end
52
+ end
@@ -1,5 +1,3 @@
1
1
  class Xattr
2
- VERSION = "0.0.3"
2
+ VERSION = "0.0.4"
3
3
  end
4
-
5
-
data/lib/ffi-xattr.rb CHANGED
@@ -14,35 +14,44 @@ end
14
14
  class Xattr
15
15
  include Enumerable
16
16
 
17
- def initialize(path)
17
+ # Create a new Xattr instance with path.
18
+ # Use <tt>:no_follow => true</tt> in options to work on symlink itself instead of following it.
19
+ def initialize(path, options = {})
18
20
  raise Errno::ENOENT, path unless File.exist?(path)
19
21
  @path = path
22
+ @no_follow = !!options[:no_follow]
20
23
  end
21
24
 
25
+ # List extended attribute names
22
26
  def list
23
- Lib.list @path
27
+ Lib.list @path, @no_follow
24
28
  end
25
29
 
30
+ # Get an extended attribute value
26
31
  def get(key)
27
- Lib.get @path, key.to_s
32
+ Lib.get @path, @no_follow, key.to_s
28
33
  end
29
34
  alias_method :[], :get
30
35
 
36
+ # Set an extended attribute value
31
37
  def set(key, value)
32
- Lib.set @path, key.to_s, value.to_s
38
+ Lib.set @path, @no_follow, key.to_s, value.to_s
33
39
  end
34
40
  alias_method :[]=, :set
35
41
 
42
+ # Remove an extended attribute value
36
43
  def remove(key)
37
- Lib.remove @path, key.to_s
44
+ Lib.remove @path, @no_follow, key.to_s
38
45
  end
39
46
 
47
+ # Iterates over pairs of extended attribute names and values
40
48
  def each(&blk)
41
49
  list.each do |key|
42
50
  yield key, get(key)
43
51
  end
44
52
  end
45
53
 
54
+ # Returns hash of extended attributes
46
55
  def as_json(*args)
47
56
  res = {}
48
57
  each { |k,v| res[k] = v }
@@ -51,5 +60,3 @@ class Xattr
51
60
  end
52
61
 
53
62
  end
54
-
55
-
data/spec/xattr_spec.rb CHANGED
@@ -35,6 +35,10 @@ describe Xattr do
35
35
  xattr.list.should == []
36
36
  end
37
37
 
38
+ it "raises error when tries to remove inexistent attribute" do
39
+ lambda { xattr.remove("inexisting") }.should raise_error
40
+ end
41
+
38
42
  it "returns nil if the attribute is not set" do
39
43
  xattr.get("hello").should be_nil
40
44
  end
@@ -71,4 +75,66 @@ describe Xattr do
71
75
  lambda { Xattr.new("no-such-file") }.should raise_error(Errno::ENOENT)
72
76
  end
73
77
 
78
+ describe "respecting :no_follow option" do
79
+ let(:link) { "link.txt" }
80
+ let(:xattr_f) { Xattr.new(link, :no_follow => false) }
81
+ let(:xattr_n) { Xattr.new(link, :no_follow => true) }
82
+
83
+ before { File.symlink(path, link) }
84
+ after { File.delete(link) }
85
+
86
+ case RUBY_PLATFORM
87
+ when /linux/
88
+ # http://linux.die.net/man/5/attr
89
+ it "should allow getting extended attributes on any type" do
90
+ xattr['user.a']
91
+ xattr_f['user.b']
92
+ xattr_n['user.c']
93
+ end
94
+
95
+ it "should fail setting extended attributes on symlink" do
96
+ lambda { xattr['user.a'] = 'foo' }.should_not raise_error
97
+ lambda { xattr_f['user.b'] = 'bar' }.should_not raise_error
98
+ lambda { xattr_n['user.c'] = 'baz' }.should raise_error
99
+ end
100
+ when /darwin|bsd/
101
+ it "should set and get attributes" do
102
+ xattr['user.a'] = 'foo'
103
+ xattr_f['user.b'] = 'bar'
104
+ xattr_n['user.c'] = 'baz'
105
+
106
+ xattr.list.sort.should == %w[user.a user.b]
107
+ xattr_f.list.sort.should == %w[user.a user.b]
108
+ xattr_n.list.sort.should == %w[user.c]
109
+
110
+ xattr['user.a'].should == 'foo'
111
+ xattr['user.b'].should == 'bar'
112
+ xattr['user.c'].should be_nil
113
+ xattr_f['user.a'].should == 'foo'
114
+ xattr_f['user.b'].should == 'bar'
115
+ xattr_f['user.c'].should be_nil
116
+ xattr_n['user.a'].should be_nil
117
+ xattr_n['user.b'].should be_nil
118
+ xattr_n['user.c'].should == 'baz'
119
+ end
120
+
121
+ it "should remove attributes" do
122
+ xattr['user.a'] = 'foo'
123
+ xattr_f['user.b'] = 'bar'
124
+ xattr_n['user.c'] = 'baz'
125
+ xattr_n['user.d'] = 'ban'
126
+
127
+ xattr.list.sort.should == %w[user.a user.b]
128
+ xattr_f.list.sort.should == %w[user.a user.b]
129
+ xattr_n.list.sort.should == %w[user.c user.d]
130
+
131
+ xattr_f.remove('user.a')
132
+ xattr_n.remove('user.c')
133
+
134
+ xattr.list.sort.should == %w[user.b]
135
+ xattr_f.list.sort.should == %w[user.b]
136
+ xattr_n.list.sort.should == %w[user.d]
137
+ end
138
+ end
139
+ end
74
140
  end
metadata CHANGED
@@ -1,46 +1,79 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: ffi-xattr
3
- version: !ruby/object:Gem::Version
4
- version: 0.0.3
3
+ version: !ruby/object:Gem::Version
4
+ hash: 23
5
5
  prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 4
10
+ version: 0.0.4
6
11
  platform: ruby
7
- authors:
12
+ authors:
8
13
  - Jari Bakken
9
14
  autorequire:
10
15
  bindir: bin
11
16
  cert_chain: []
12
- date: 2011-09-24 00:00:00.000000000Z
13
- dependencies:
14
- - !ruby/object:Gem::Dependency
15
- name: rspec
16
- requirement: &2164580140 !ruby/object:Gem::Requirement
17
+
18
+ date: 2011-11-15 00:00:00 Z
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ type: :runtime
22
+ requirement: &id001 !ruby/object:Gem::Requirement
17
23
  none: false
18
- requirements:
19
- - - ~>
20
- - !ruby/object:Gem::Version
21
- version: '2.5'
22
- type: :development
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ hash: 3
28
+ segments:
29
+ - 0
30
+ version: "0"
23
31
  prerelease: false
24
- version_requirements: *2164580140
25
- - !ruby/object:Gem::Dependency
26
32
  name: ffi
27
- requirement: &2164578540 !ruby/object:Gem::Requirement
33
+ version_requirements: *id001
34
+ - !ruby/object:Gem::Dependency
35
+ type: :development
36
+ requirement: &id002 !ruby/object:Gem::Requirement
28
37
  none: false
29
- requirements:
30
- - - ! '>='
31
- - !ruby/object:Gem::Version
32
- version: '0'
33
- type: :runtime
38
+ requirements:
39
+ - - ~>
40
+ - !ruby/object:Gem::Version
41
+ hash: 9
42
+ segments:
43
+ - 2
44
+ - 5
45
+ version: "2.5"
46
+ prerelease: false
47
+ name: rspec
48
+ version_requirements: *id002
49
+ - !ruby/object:Gem::Dependency
50
+ type: :development
51
+ requirement: &id003 !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ~>
55
+ - !ruby/object:Gem::Version
56
+ hash: 63
57
+ segments:
58
+ - 0
59
+ - 9
60
+ - 2
61
+ version: 0.9.2
34
62
  prerelease: false
35
- version_requirements: *2164578540
63
+ name: rake
64
+ version_requirements: *id003
36
65
  description: Manipulate extended file attributes
37
- email:
66
+ email:
38
67
  - jari.bakken@gmail.com
39
68
  executables: []
69
+
40
70
  extensions: []
71
+
41
72
  extra_rdoc_files: []
42
- files:
73
+
74
+ files:
43
75
  - .gitignore
76
+ - .travis.yml
44
77
  - Gemfile
45
78
  - LICENSE
46
79
  - README.md
@@ -54,27 +87,36 @@ files:
54
87
  - spec/xattr_spec.rb
55
88
  homepage: http://github.com/jarib/ffi-xattr
56
89
  licenses: []
90
+
57
91
  post_install_message:
58
92
  rdoc_options: []
59
- require_paths:
93
+
94
+ require_paths:
60
95
  - lib
61
- required_ruby_version: !ruby/object:Gem::Requirement
96
+ required_ruby_version: !ruby/object:Gem::Requirement
62
97
  none: false
63
- requirements:
64
- - - ! '>='
65
- - !ruby/object:Gem::Version
66
- version: '0'
67
- required_rubygems_version: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ hash: 3
102
+ segments:
103
+ - 0
104
+ version: "0"
105
+ required_rubygems_version: !ruby/object:Gem::Requirement
68
106
  none: false
69
- requirements:
70
- - - ! '>='
71
- - !ruby/object:Gem::Version
72
- version: '0'
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ hash: 3
111
+ segments:
112
+ - 0
113
+ version: "0"
73
114
  requirements: []
115
+
74
116
  rubyforge_project: ffi-xattr
75
117
  rubygems_version: 1.8.10
76
118
  signing_key:
77
119
  specification_version: 3
78
120
  summary: Manipulate extended file attributes
79
- test_files:
121
+ test_files:
80
122
  - spec/xattr_spec.rb