views:

90

answers:

4

I am trying to understand the Python "ctypes" module. I have put together a trivial example that -- ideally -- wraps the statvfs() function call. The code looks like this:

from ctypes import *

class struct_statvfs (Structure):
    _fields_ = [
            ('f_bsize', c_ulong),
            ('f_frsize', c_ulong),
            ('f_blocks', c_ulong),
            ('f_bfree', c_ulong),
            ('f_bavail', c_ulong),
            ('f_files', c_ulong),
            ('f_ffree', c_ulong),
            ('f_favail', c_ulong),
            ('f_fsid', c_ulong),
            ('f_flag', c_ulong),
            ('f_namemax', c_ulong),
            ]


libc = CDLL('libc.so.6')
libc.statvfs.argtypes = [c_char_p, POINTER(struct_statvfs)]
s = struct_statvfs()

res = libc.statvfs('/etc', byref(s))
print 'return = %d, f_bsize = %d, f_blocks = %d, f_bfree = %d' % (
    res, s.f_bsize, s.f_blocks, s.f_bfree)

Running this invariably returns:

return = 0, f_bsize = 4096, f_blocks = 10079070, f_bfree = 5048834
*** glibc detected *** python: free(): invalid next size (fast): 0x0000000001e51780 ***
*** glibc detected *** python: malloc(): memory corruption (fast): 0x0000000001e517e0 ***

I haven't been able to find any examples of calling functions with complex types as parameters (there are lots of examples of functions that return complex types), but after staring at the ctypes documentation for a day or so I think my calling syntax is correct...and it is actually callling the statvfs() call and getting back correct results.

Am I misunderstanding the ctypes docs? Or is something else going on here?

Thanks!

+2  A: 

The manpage for statvfs states that the struct it uses is "defined approximately as follows", so you can't necessarily take the manpage's field listings as complete.

My guess is that there are additional struct fields after the end of the struct as you've defined it. This causes the statvfs function to overwrite memory outside your struct. I made the problem go away for me by adding a huge padding field to the _fields_ in my struct definition:

("padding", c_int * 1000),

Keep in mind that my script manifested the problem differently than yours; I got a segfault, whereas you merely got some error messages. Still, I'm guessing that it's the same problem, so you should try adding some padding and see if the problem persists.

Eli Courtwright
c_int * 6 seems to be enough (but * 1000 is safer, * 100 should be safe enough), see /usr/include/bits/statvfs.h on Linux: struct statvfs contains int __f_spare[6];
pts
+2  A: 

As Eli indicates, looking in /usr/include/bits/statvfs.h, your struct is not defined properly.

On my 64 bit Gentoo system it would be:

 class struct_statvfs (Structure):
    _fields_ = [
            ('f_bsize', c_ulong),
            ('f_frsize', c_ulong),
            ('f_blocks', c_ulong),
            ('f_bfree', c_ulong),
            ('f_bavail', c_ulong),
            ('f_files', c_ulong),
            ('f_ffree', c_ulong),
            ('f_favail', c_ulong),
            ('f_fsid', c_ulong),
            ('f_flag', c_ulong),
            ('f_namemax', c_ulong),
            ('__f_space', c_int * 6) # you are missing this
            ]
Mark
A: 

According to the successful pyfuse ctypes wrappers for Fuse, the following is used for struct statvfs on Linux:

class c_statvfs(Structure):
    _fields_ = [
        ('f_bsize', c_ulong),
        ('f_frsize', c_ulong),
        ('f_blocks', c_fsblkcnt_t),
        ('f_bfree', c_fsblkcnt_t),
        ('f_bavail', c_fsblkcnt_t),
        ('f_files', c_fsfilcnt_t),
        ('f_ffree', c_fsfilcnt_t),
        ('f_favail', c_fsfilcnt_t)]

More info: http://code.google.com/p/fusepy/

Noah Watkins
Might want to take a look a patch to fusepy that I submitted that contains corrections to their statvfs. http://code.google.com/p/fusepy/issues/detail?id=26
Matt Joiner
Ahh nice. Didn't realize it was out of date. Btw, I noticed that fusepy only has a hook for statfs, not statvfs.
Noah Watkins
+4  A: 

Execute this command to get the exact definition of struct statvfs on your system:

echo '#include <sys/statvfs.h>' | gcc -E - | less

Then press /struct statvfs<enter> to skip to the definition and browse from there.

Also take a look at my patch to fusepy, and their definition.

Matt Joiner
That's what I get for trusting the man page. Thanks for the suggestion! Since this was my first time working with ctypes I wasn't sure exactly what the failure mode meant.
larsks
@matt, cool short cut for getting the struct definition. I gotta remember that.
Mark
Yes, ctypes is talked up a lot, but it's still painful to get things all defined correctly. It really doesn't help that a C compiler is kind of necessary. There should be an official ABI standard that C and other languages can mutually depend on.
Matt Joiner