diff --git a/ChangeLog b/ChangeLog index 363dc9b5c1..cd0562b24d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +Tue Apr 22 19:32:48 2014 NARUSE, Yui + + * file.c: newly added a class File::Statfs. (experimental) + Tue Apr 22 08:22:33 2014 Koichi Sasada * gc.c (objspace_malloc_increase): don't cause GC by malloc_increase diff --git a/NEWS b/NEWS index 0b999aa027..cbe70d0376 100644 --- a/NEWS +++ b/NEWS @@ -20,6 +20,13 @@ with all sufficient information, see the ChangeLog file. * min, min_by, max and max_by supports optional argument to return multiple elements. +* File + * new class File::Statfs to hold filesystem information. (experimental) + +* IO + * New methods + * IO#statfs returns filesystem information as File::Statfs. (experimental) + * Symbol * New methods * Symbol.find(str) returns whether given string is defined as symbol or not. diff --git a/configure.in b/configure.in index 18d58d85ea..44b7e2c3ed 100644 --- a/configure.in +++ b/configure.in @@ -1116,10 +1116,12 @@ AC_CHECK_HEADERS( \ sys/syscall.h \ fcntl.h \ sys/fcntl.h \ + sys/mount.h \ sys/select.h \ sys/time.h \ sys/times.h \ sys/param.h \ + sys/vfs.h \ syscall.h \ pwd.h \ grp.h \ @@ -1719,6 +1721,25 @@ AC_CHECK_TYPES([struct timezone], [], [], [@%:@ifdef HAVE_TIME_H @%:@ include @%:@endif]) +AC_CHECK_TYPES([struct statfs], [], [], [@%:@ifdef HAVE_SYS_PARAM_H +@%:@ include +@%:@endif +@%:@ifdef HAVE_SYS_MOUNT_H +@%:@ include +@%:@endif +@%:@ifdef HAVE_SYS_VFS_H +@%:@ include +@%:@endif]) +AC_CHECK_MEMBERS([struct statfs.f_fstypename], [], [], [@%:@ifdef HAVE_SYS_PARAM_H +@%:@ include +@%:@endif +@%:@ifdef HAVE_SYS_MOUNT_H +@%:@ include +@%:@endif +@%:@ifdef HAVE_SYS_VFS_H +@%:@ include +@%:@endif]) + AC_CHECK_TYPES([clockid_t], [], [], [@%:@ifdef HAVE_TIME_H @%:@ include @%:@endif diff --git a/file.c b/file.c index 89e1694d56..6de2feaa64 100644 --- a/file.c +++ b/file.c @@ -63,6 +63,14 @@ int flock(int, int); #include #include +#ifdef HAVE_SYS_MOUNT_H +#include +#endif +#ifdef HAVE_SYS_VFS_H +#include +#endif +VALUE rb_statfs_new(const struct statfs *st); + #if defined(__native_client__) && defined(NACL_NEWLIB) # include "nacl/utime.h" # include "nacl/stat.h" @@ -139,6 +147,7 @@ be_fchown(int fd, uid_t owner, gid_t group) VALUE rb_cFile; VALUE rb_mFileTest; VALUE rb_cStat; +VALUE rb_cStatfs; #define insecure_obj_p(obj, level) ((level) >= 4 || ((level) > 0 && OBJ_TAINTED(obj))) @@ -1086,6 +1095,34 @@ rb_file_lstat(VALUE obj) #endif } +/* + * call-seq: + * ios.statfs -> statfs + * + * Returns filesystem status information for ios as an object of type + * File::Statfs. + * + * f = File.new("testfile") + * s = f.statfs + * s.mode #=> "100644" + * s.bsize #=> 512 + * s.fstypename #=> "zfs" + * + */ + +static VALUE +rb_io_statfs(VALUE obj) +{ + rb_io_t *fptr; + struct statfs st; + + GetOpenFile(obj, fptr); + if (fstatfs(fptr->fd, &st) == -1) { + rb_sys_fail_path(fptr->pathv); + } + return rb_statfs_new(&st); +} + static int rb_group_member(GETGROUPS_T gid) { @@ -5263,6 +5300,254 @@ rb_stat_sticky(VALUE obj) return Qfalse; } +/* File::Statfs */ + +static size_t +statfs_memsize(const void *p) +{ + return p ? sizeof(struct statfs) : 0; +} + +static const rb_data_type_t statfs_data_type = { + "statfs", + {NULL, RUBY_TYPED_DEFAULT_FREE, statfs_memsize,}, + NULL, NULL, RUBY_TYPED_FREE_IMMEDIATELY +}; + +static VALUE +statfs_new_0(VALUE klass, const struct statfs *st) +{ + struct statfs *nst = 0; + + if (st) { + nst = ALLOC(struct statfs); + *nst = *st; + } + return TypedData_Wrap_Struct(klass, &statfs_data_type, nst); +} + +VALUE +rb_statfs_new(const struct statfs *st) +{ + return statfs_new_0(rb_cStatfs, st); +} + +static struct statfs* +get_statfs(VALUE self) +{ + struct statfs* st; + TypedData_Get_Struct(self, struct statfs, &statfs_data_type, st); + if (!st) rb_raise(rb_eTypeError, "uninitialized File::Statfs"); + return st; +} + +/* + * Document-class: File::Statfs + * + * Objects of class File::Statfs encapsulate common status + * information for filesystem. The information is + * recorded at the moment the File::Statfs object is + * created; changes made to the filesystem after that point will not be + * reflected. File::Statfs objects are returned by + * IO#statfs. + */ + +static VALUE +rb_statfs_s_alloc(VALUE klass) +{ + return statfs_new_0(klass, 0); +} + +/* + * call-seq: + * + * File::Statfs.new(file_name) -> statfs + * + * Create a File::Statfs object for the given file name (raising an + * exception if the file doesn't exist). + */ + +static VALUE +rb_statfs_init(VALUE obj, VALUE fname) +{ + struct statfs st, *nst; + + rb_secure(2); + FilePathValue(fname); + fname = rb_str_encode_ospath(fname); + if (statfs(StringValueCStr(fname), &st) == -1) { + rb_sys_fail_path(fname); + } + if (DATA_PTR(obj)) { + xfree(DATA_PTR(obj)); + DATA_PTR(obj) = NULL; + } + nst = ALLOC(struct statfs); + *nst = st; + DATA_PTR(obj) = nst; + + return Qnil; +} + +/* :nodoc: */ +static VALUE +rb_statfs_init_copy(VALUE copy, VALUE orig) +{ + struct statfs *nst; + + if (!OBJ_INIT_COPY(copy, orig)) return copy; + if (DATA_PTR(copy)) { + xfree(DATA_PTR(copy)); + DATA_PTR(copy) = 0; + } + if (DATA_PTR(orig)) { + nst = ALLOC(struct statfs); + *nst = *(struct statfs*)DATA_PTR(orig); + DATA_PTR(copy) = nst; + } + + return copy; +} + +/* + * call-seq: + * st.type -> fixnum + * + * Returns type of filesystem. + * + * f = File.new("testfile") + * s = f.statfs + * "%d" % s.type #=> 17 + * + */ + +static VALUE +statfs_type(VALUE self) +{ + return LL2NUM(get_statfs(self)->f_type); +} + +/* + * call-seq: + * st.bsize -> integer + * + * Returns block size in filesystem. + * + */ + +static VALUE +statfs_bsize(VALUE self) +{ + return LL2NUM(get_statfs(self)->f_bsize); +} + +/* + * call-seq: + * st.blocks -> integer + * + * Returns total data bocks of filesystem. + * + */ + +static VALUE +statfs_blocks(VALUE self) +{ + return LL2NUM(get_statfs(self)->f_blocks); +} + +/* + * call-seq: + * st.bfree -> integer + * + * Returns free blocks in filesystem. + * + */ + +static VALUE +statfs_bfree(VALUE self) +{ + return LL2NUM(get_statfs(self)->f_bfree); +} + +/* + * call-seq: + * st.bavail -> integer + * + * Returns available blocks to non-super user in filesystem. + * + */ + +static VALUE +statfs_bavail(VALUE self) +{ + return LL2NUM(get_statfs(self)->f_bavail); +} + +/* + * call-seq: + * st.files -> integer + * + * Returns total file nodes in filesystem. + * + */ + +static VALUE +statfs_files(VALUE self) +{ + return LL2NUM(get_statfs(self)->f_files); +} + +/* + * call-seq: + * st.ffree -> integer + * + * Returns free nodes in filesystem. + * + */ + +static VALUE +statfs_ffree(VALUE self) +{ + return LL2NUM(get_statfs(self)->f_ffree); +} + +/* + * call-seq: + * st.fsid -> integer + * + * Returns filesystem id. + * + */ + +static VALUE +statfs_fsid(VALUE self) +{ + fsid_t n = get_statfs(self)->f_fsid; + return LL2NUM(*(LONG_LONG*)&n); +} + +#ifdef HAVE_STRUCT_STATFS_F_FSTYPENAME +/* + * call-seq: + * st.fstypename -> string + * + * Returns name of filesystem. + * + * f = File.new("testfile") + * s = f.statfs + * s.fstypename #=> "zfs" + * + */ + +static VALUE +statfs_fstypename(VALUE self) +{ + return rb_str_new_cstr(get_statfs(self)->f_fstypename); +} +#else +#define statfs_fsname rb_f_notimplement +#endif + VALUE rb_mFConst; void @@ -5689,6 +5974,7 @@ Init_File(void) rb_define_const(rb_cFile, "PATH_SEPARATOR", rb_obj_freeze(rb_str_new2(PATH_SEP))); rb_define_method(rb_cIO, "stat", rb_io_stat, 0); /* this is IO's method */ + rb_define_method(rb_cIO, "statfs", rb_io_statfs, 0); /* this is IO's method */ rb_define_method(rb_cFile, "lstat", rb_file_lstat, 0); rb_define_method(rb_cFile, "atime", rb_file_atime, 0); @@ -5845,4 +6131,18 @@ Init_File(void) rb_define_method(rb_cStat, "setuid?", rb_stat_suid, 0); rb_define_method(rb_cStat, "setgid?", rb_stat_sgid, 0); rb_define_method(rb_cStat, "sticky?", rb_stat_sticky, 0); + + rb_cStatfs = rb_define_class_under(rb_cFile, "Statfs", rb_cObject); + rb_define_alloc_func(rb_cStatfs, rb_statfs_s_alloc); + rb_define_method(rb_cStatfs, "initialize", rb_statfs_init, 1); + rb_define_method(rb_cStatfs, "initialize_copy", rb_statfs_init_copy, 1); + rb_define_method(rb_cStatfs, "type", statfs_type, 0); + rb_define_method(rb_cStatfs, "bsize", statfs_bsize, 0); + rb_define_method(rb_cStatfs, "blocks", statfs_blocks, 0); + rb_define_method(rb_cStatfs, "bfree", statfs_bfree, 0); + rb_define_method(rb_cStatfs, "bavail", statfs_bavail, 0); + rb_define_method(rb_cStatfs, "files", statfs_files, 0); + rb_define_method(rb_cStatfs, "ffree", statfs_ffree, 0); + rb_define_method(rb_cStatfs, "fsid", statfs_fsid, 0); + rb_define_method(rb_cStatfs, "fstypename", statfs_fstypename, 0); } diff --git a/test/ruby/test_file.rb b/test/ruby/test_file.rb index 01992a83d4..9775c91ce3 100644 --- a/test/ruby/test_file.rb +++ b/test/ruby/test_file.rb @@ -383,4 +383,23 @@ class TestFile < Test::Unit::TestCase assert_file.not_exist?(path) end end + + def test_statfs + open(__FILE__) do |f| + st = f.statfs + assert_kind_of File::Statfs, st + assert_kind_of Integer, st.type + assert_kind_of Integer, st.bsize + assert_kind_of Integer, st.blocks + assert_kind_of Integer, st.bfree + assert_kind_of Integer, st.bavail + assert_kind_of Integer, st.files + assert_kind_of Integer, st.ffree + assert_kind_of Integer, st.fsid + begin + assert_kind_of String, st.fstypename + rescue NotImplementedError + end + end + end end