jarib / ffi-xattr

Manipulate extended file attributes
Apache License 2.0
27 stars 6 forks source link

Add support for f*xattr #5

Open pieterb opened 11 years ago

pieterb commented 11 years ago

Currently, the gem supports listxattr, getxattr, setxattr and removexattr for manipulating extended attributes on files, and l*xattr for manipulating symbolic links. However, on my platform there's also the function family f*xattr, to manipulate extended attributes on file descriptors.

targetsetlistgetremove
file setxattr listxattr getxattr removexattr
symbolic link lsetxattr llistxattr lgetxattr lremovexattr
file descriptor fsetxattr flistxattr fgetxattr fremovexattr

I tried to create a patch, but didn't get it to work because I don't know enough about ffi. I'll try to append a patch file to this issue, so you can see what I'm trying to achieve.

pieterb commented 11 years ago

Hmpf... I can't attach the patch as a file, but here it is:

diff --git a/lib/ffi-xattr.rb b/lib/ffi-xattr.rb
index 0b8ddcb..82e77b3 100644
--- a/lib/ffi-xattr.rb
+++ b/lib/ffi-xattr.rb
@@ -3,7 +3,7 @@ require 'ffi-xattr/version'
 require 'ffi-xattr/error'

 case RUBY_PLATFORM
-when /linux/
+when /linux/, /java/i
   require 'ffi-xattr/linux_lib'
 when /darwin|bsd/
   require 'ffi-xattr/darwin_lib'
@@ -17,8 +17,12 @@ class Xattr
   # Create a new Xattr instance with path.
   # Use <tt>:no_follow => true</tt> in options to work on symlink itself instead of following it.
   def initialize(path, options = {})
-    raise Errno::ENOENT, path unless File.exist?(path)
-    @path = path.to_str
+    if path.respond_to? :fileno
+      @path = path.fileno
+    else
+      @path = path.to_str
+      raise Errno::ENOENT, path unless File.exist?(@path)
+    end
     @no_follow = !!options[:no_follow]
   end

diff --git a/lib/ffi-xattr/darwin_lib.rb b/lib/ffi-xattr/darwin_lib.rb
index 59f0810..92850bf 100644
--- a/lib/ffi-xattr/darwin_lib.rb
+++ b/lib/ffi-xattr/darwin_lib.rb
@@ -4,42 +4,71 @@ class Xattr # :nodoc: all

     ffi_lib "System"

-    attach_function :listxattr,   [:string, :pointer, :size_t, :int], :ssize_t
-    attach_function :getxattr,    [:string, :string, :pointer, :size_t, :uint, :int], :ssize_t
-    attach_function :setxattr,    [:string, :string, :pointer, :size_t, :uint, :int], :int
-    attach_function :removexattr, [:string, :string, :int], :int
+    attach_function :listxattr,    [:string, :pointer, :size_t, :int], :ssize_t
+    attach_function :getxattr,     [:string, :string, :pointer, :size_t, :uint, :int], :ssize_t
+    attach_function :setxattr,     [:string, :string, :pointer, :size_t, :uint, :int], :int
+    attach_function :removexattr,  [:string, :string, :int], :int
+
+    attach_function :flistxattr,   [:int, :pointer, :size_t, :int], :ssize_t
+    attach_function :fgetxattr,    [:int, :string, :pointer, :size_t, :uint, :int], :ssize_t
+    attach_function :fsetxattr,    [:int, :string, :pointer, :size_t, :uint, :int], :int
+    attach_function :fremovexattr, [:int, :string, :int], :int

     XATTR_NOFOLLOW = 0x0001

     class << self
       def list(path, no_follow)
-        options = no_follow ? XATTR_NOFOLLOW : 0
-        size = listxattr(path, nil, 0, options)
+        if path.kind_of?(Integer)
+          options = 0
+          method = :flistxattr
+        else
+          options = no_follow ? XATTR_NOFOLLOW : 0
+          method = :listxattr
+        end
+        size = send(method, path, nil, 0, options)
         res_ptr = FFI::MemoryPointer.new(:pointer, size)
-        listxattr(path, res_ptr, size, options)
+        send(method, path, res_ptr, size, options)

         res_ptr.read_string(size).split("\000")
       end

       def get(path, no_follow, key)
-        options = no_follow ? XATTR_NOFOLLOW : 0
-        size = getxattr(path, key, nil, 0, 0, options)
+        if path.kind_of?(Integer)
+          options = 0
+          method = :fgetxattr
+        else
+          options = no_follow ? XATTR_NOFOLLOW : 0
+          method = :getxattr
+        end
+        size = send(method, path, key, nil, 0, 0, options)
         return unless size > 0

         str_ptr = FFI::MemoryPointer.new(:char, size)
-        getxattr(path, key, str_ptr, size, 0, options)
+        send(method, path, key, str_ptr, size, 0, options)

         str_ptr.read_string(size)
       end

       def set(path, no_follow, key, value)
-        options = no_follow ? XATTR_NOFOLLOW : 0
-        Error.check setxattr(path, key, value, value.bytesize, 0, options)
+        if path.kind_of?(Integer)
+          options = 0
+          method = :fsetxattr
+        else
+          options = no_follow ? XATTR_NOFOLLOW : 0
+          method = :setxattr
+        end
+        Error.check send(method, path, key, value, value.bytesize, 0, options)
       end

       def remove(path, no_follow, key)
-        options = no_follow ? XATTR_NOFOLLOW : 0
-        Error.check removexattr(path, key, options)
+        if path.kind_of?(Integer)
+          options = 0
+          method = :fremovexattr
+        else
+          options = no_follow ? XATTR_NOFOLLOW : 0
+          method = :removexattr
+        end
+        Error.check send(method, path, key, options)
       end
     end

diff --git a/lib/ffi-xattr/linux_lib.rb b/lib/ffi-xattr/linux_lib.rb
index e90a8ad..a7a9718 100644
--- a/lib/ffi-xattr/linux_lib.rb
+++ b/lib/ffi-xattr/linux_lib.rb
@@ -16,10 +16,18 @@ class Xattr # :nodoc: all
     attach_function :lgetxattr,    [:string, :string, :pointer, :size_t], :int
     attach_function :lremovexattr, [:string, :string], :int

+    attach_function :flistxattr,   [:int, :pointer, :size_t], :size_t
+    attach_function :fsetxattr,    [:int, :string, :pointer, :size_t, :int], :int
+    attach_function :fgetxattr,    [:int, :string, :pointer, :size_t], :int
+    attach_function :fremovexattr, [:int, :string], :int
+
     class << self
       def list(path, no_follow)
-        method = no_follow ? :llistxattr : :listxattr
-        size = send(method, path, nil, 0)
+        method = path.kind_of?(Integer) ? :flistxattr :
+          no_follow ? :llistxattr : :listxattr
+        $stderr.puts method.to_s
+        Error.check( size = send(method, path, nil, 0) )
+        $stderr.puts size.to_s
         res_ptr = FFI::MemoryPointer.new(:pointer, size)
         send(method, path, res_ptr, size)

@@ -27,7 +35,8 @@ class Xattr # :nodoc: all
       end

       def get(path, no_follow, key)
-        method = no_follow ? :lgetxattr : :getxattr
+        method = path.kind_of?(Integer) ? :fgetxattr :
+          no_follow ? :lgetxattr : :getxattr
         size = send(method, path, key, nil, 0)
         return unless size > 0

@@ -38,12 +47,14 @@ class Xattr # :nodoc: all
       end

       def set(path, no_follow, key, value)
-        method = no_follow ? :lsetxattr : :setxattr
+        method = path.kind_of?(Integer) ? :fsetxattr :
+          no_follow ? :lsetxattr : :setxattr
         Error.check send(method, path, key, value, value.bytesize, 0)
       end

       def remove(path, no_follow, key)
-        method = no_follow ? :lremovexattr : :removexattr
+        method = path.kind_of?(Integer) ? :fremovexattr :
+          no_follow ? :lremovexattr : :removexattr
         Error.check send(method, path, key)
       end
     end