From: Hongli Lai Date: 2008-03-07T20:32:02+09:00 Subject: PATCH: mknod and mkfifo support [Was: Ruby does not support mkfifo] This is a multi-part message in MIME format. --------------020500020902060205000805 Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit In my last email, I mentioned that there was an old patch that implements mknod() and mkfifo() support. That patch no longer works. I've ported the patch to Ruby 1.9, improved its documentation, and wrote unit tests. Please review the attached patch. Regards, Hongli Lai --------------020500020902060205000805 Content-Type: text/x-patch; name="ruby-mkfifo-mknod.diff" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="ruby-mkfifo-mknod.diff" diff --git a/configure.in b/configure.in index c178210..2df4f6c 100644 --- a/configure.in +++ b/configure.in @@ -661,7 +661,7 @@ AC_CHECK_FUNCS(fmod killpg wait4 waitpid fork spawnv syscall chroot fsync getcwd dlopen sigprocmask sigaction sigsetjmp _setjmp vsnprintf snprintf\ setsid telldir seekdir fchmod cosh sinh tanh log2 round\ setuid setgid daemon select_large_fdset setenv unsetenv\ - mktime timegm clock_gettime gettimeofday) + mktime timegm clock_gettime gettimeofday mkfifo mknod) AC_ARG_ENABLE(setreuid, [ --enable-setreuid use setreuid()/setregid() according to need even if obsolete.], [use_setreuid=$enableval]) diff --git a/file.c b/file.c index c32afbc..df8d656 100644 --- a/file.c +++ b/file.c @@ -3509,6 +3509,114 @@ rb_f_test(int argc, VALUE *argv) return Qnil; /* not reached */ } +/* + * call-seq: + * File.mknod(file_name, [type, [mode, [dev]]]) => 0 + * + * Create a filesystem node (file, device special file or named pipe) + * named _file_name_, specified by _mode_ and _dev_. + * + * _type_ and _mode_ specifies the type and the permissions of node to + * be created respectively. + * + * The permissions are modified by the process's umask in the usual + * way: the permissions of the created node are (mode & ~umask). + * + * _type_ should be one of: + * - +?f+ - normal file (which will be created empty) + * - +?c+ - character special file + * - +?b+ - block special file + * - +?p+ - FIFO (named pipe) + * - +nil+ - normal file + * + * Raises: + * - +ArgumentError+ - _type_ is unknown or not supported on the current platform. + * - +SystemCallError+ - Something went wrong during the creation of the node. + * - +NotImplementedError+ - This call is not supported on the current platform. + */ + +static VALUE +rb_file_s_mknod(argc, argv) + int argc; + VALUE *argv; +{ +#ifdef HAVE_MKNOD + VALUE path, type, vmode, vdev; + int mode = 0666, dev = 0, t; + + rb_secure(4); + switch (rb_scan_args(argc, argv, "13", &path, &type, &vmode, &vdev)) { + case 4: + dev = NUM2INT(vdev); + case 3: + mode = NUM2INT(vmode) & ~S_IFMT; + } + FilePathValue(path); + if (!NIL_P(type)) { + rb_check_safe_obj(type); + switch (t = NUM2CHR(type)) { + case 'f': mode |= S_IFREG; break; + case 'c': mode |= S_IFCHR; break; +#ifdef S_IFBLK + case 'b': mode |= S_IFBLK; break; +#endif +#ifdef S_IFIFO + case 'p': mode |= S_IFIFO; break; +#endif + default: + rb_raise(rb_eArgError, "unknown node type - %c", t); + } + } + if (mknod(StringValueCStr(path), mode, dev)) { + rb_sys_fail("mknod"); + } +#else + rb_notimplement(); +#endif + return INT2FIX(0); +} + +/* + * call-seq: + * File.mkfifo(file_name, mode) => 0 + * + * Creates a FIFO special file (named pipe) with name _file_name_. _mode_ + * specifies the FIFO's permissions. It is modified by the process's + * umask in the usual way: the permissions of the created file are + * (mode & ~umask). + * + * Raises: + * - +SystemCallError+ - Something went wrong during the creation of the FIFO. + * - +NotImplementedError+ - This call is not supported on the current platform. + */ + +static VALUE +rb_file_s_mkfifo(argc, argv) + int argc; + VALUE *argv; +{ +#if !defined HAVE_MKFIFO && defined HAVE_MKNOD && defined S_IFIFO + #define mkfifo(path, mode) mknod(path, (mode)&~S_IFMT|S_IFIFO, 0) + #define HAVE_MKFIFO +#endif +#ifdef HAVE_MKFIFO + VALUE path, vmode; + int mode = 0666; + + rb_secure(4); + if (rb_scan_args(argc, argv, "11", &path, &vmode) > 1) { + mode = NUM2INT(vmode); + } + FilePathValue(path); + if (mkfifo(StringValueCStr(path), mode)) { + rb_sys_fail("mkfifo"); + } +#else + rb_notimplement(); +#endif + return INT2FIX(0); +} + /* @@ -4512,6 +4620,8 @@ Init_File(void) rb_define_singleton_method(rb_cFile, "rename", rb_file_s_rename, 2); rb_define_singleton_method(rb_cFile, "umask", rb_file_s_umask, -1); rb_define_singleton_method(rb_cFile, "truncate", rb_file_s_truncate, 2); + rb_define_singleton_method(rb_cFile, "mknod", rb_file_s_mknod, -1); + rb_define_singleton_method(rb_cFile, "mkfifo", rb_file_s_mkfifo, -1); rb_define_singleton_method(rb_cFile, "expand_path", rb_file_s_expand_path, -1); rb_define_singleton_method(rb_cFile, "basename", rb_file_s_basename, -1); rb_define_singleton_method(rb_cFile, "dirname", rb_file_s_dirname, 1); diff --git a/test/ruby/test_file_exhaustive.rb b/test/ruby/test_file_exhaustive.rb index d29253f..45b2981 100644 --- a/test/ruby/test_file_exhaustive.rb +++ b/test/ruby/test_file_exhaustive.rb @@ -8,12 +8,21 @@ class TestFileExhaustive < Test::Unit::TestCase @file = make_tmp_filename("file") @zerofile = make_tmp_filename("zerofile") @nofile = make_tmp_filename("nofile") + @fifofile_nod = make_tmp_filename("fifofile_nod") + @fifofile = make_tmp_filename("fifofile") @symlinkfile = make_tmp_filename("symlinkfile") @hardlinkfile = make_tmp_filename("hardlinkfile") make_file("foo", @file) make_file("", @zerofile) @time = Time.now begin + File.mkfifo(@fifofile, 0600) + File.mknod(@fifofile_nod, "p") + rescue NotImplementedError + @fifofile = nil + @fifofile_nod = nil + end + begin File.symlink(@file, @symlinkfile) rescue NotImplementedError @symlinkfile = nil @@ -104,6 +113,7 @@ class TestFileExhaustive < Test::Unit::TestCase assert(!(File.pipe?(@dir))) assert(!(File.pipe?(@file))) assert(!(File.pipe?(@nofile))) + assert(File.pipe?(@fifofile)) if @fifofile end def test_symlink_p @@ -265,6 +275,7 @@ class TestFileExhaustive < Test::Unit::TestCase assert_equal("file", File.ftype(@file)) assert_equal("link", File.ftype(@symlinkfile)) if @symlinkfile assert_equal("file", File.ftype(@hardlinkfile)) if @hardlinkfile + assert_equal("fifo", File.ftype(@fifofile)) if @fifofile assert_raise(Errno::ENOENT) { File.ftype(@nofile) } end @@ -321,6 +332,16 @@ class TestFileExhaustive < Test::Unit::TestCase def test_lchown ## xxx end + + def test_mkfifo_and_mknod + return unless @fifofile + assert_equal("fifo", File.ftype(@fifofile)) + assert_equal("fifo", File.ftype(@fifofile_nod)) + + nod_normal = make_tmp_filename("nod_normal") + File.mknod(nod_normal) + assert_equal("file", File.ftype(nod_normal)) + end def test_symlink return unless @symlinkfile --------------020500020902060205000805--