Monday, August 11, 2014

How to implement statfs syscall for Ruby 1.8 (pre Fiddle)

Here is statfs implementation for older Ruby releases that don't have Fiddle:

#!/usr/bin/ruby

# gcc -x c -E <(echo "#include <sys/vfs.h>") | less
# struct statfs {
#   long int f_type
#   long int f_bsize
#   unsigned long int f_blocks
#   unsigned long int f_bfree
#   unsigned long int f_bavail
#   unsigned long int f_files
#   unsigned long int f_ffree
#   struct { int __val[2] } f_fsid
#   long int f_namelen
#   long int f_frsize
#   long int f_flags
#   long int f_spare[4]
# }
# extern int statfs (const char *__file, struct statfs *__buf)

require 'ostruct'

module Sys
  # /usr/include/asm-x86_64/unistd.h
  SYS_STATFS = 137

  SENTINEL = 0x91d210f49de98115
  FMT_STATFS = "Q16"

  # Use SENTINEL to check that syscall doesn't overflow the buffer.
  # BTW there is a bug in newer (post 1.8.6) ruby's syscall: you cannot
  # provide buffer with NUL octets :( Luckily in this case you can fill
  # the whole buffer with non-NUL octets because buffer is only used
  # for returning data from the syscall

  def self.statfs(filename)
    buf = ([SENTINEL] * 16).pack FMT_STATFS
    syscall SYS_STATFS, filename, buf

    k = %w{type bsize blocks bfree bavail files ffree fsid namelen frsize
           flags spare1 spare2 spare3 spare4}
    v = buf.unpack FMT_STATFS
    raise TypeError if v.pop != SENTINEL

    OpenStruct.new k.zip(v)
  end
end

st = Sys.statfs('/tmp')
puts st.inspect

Output:

#<OpenStruct bavail=212090654, type=61267, flags=4128, files=60506112,
spare1=0, blocks=238218731, ffree=59367476, spare2=0, fsid=15391897869155704811,
spare3=0, bsize=4096, namelen=255, spare4=0, bfree=224191466, frsize=4096>

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.