ruby-changes:29730
From: knu <ko1@a...>
Date: Fri, 5 Jul 2013 02:22:18 +0900 (JST)
Subject: [ruby-changes:29730] knu:r41782 (trunk): * lib/fileutils.rb (FileUtils.chmod{,_R}): Enhance the symbolic
knu 2013-07-05 02:22:08 +0900 (Fri, 05 Jul 2013) New Revision: 41782 http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=rev&revision=41782 Log: * lib/fileutils.rb (FileUtils.chmod{,_R}): Enhance the symbolic mode parser to support the permission symbols u/g/o and multiple actions as defined in SUS, so that chmod("g=o+w", file) works as expected. Invalid symbolic modes are now rejected with ArgumentError. Modified files: trunk/ChangeLog trunk/lib/fileutils.rb trunk/test/fileutils/test_fileutils.rb Index: ChangeLog =================================================================== --- ChangeLog (revision 41781) +++ ChangeLog (revision 41782) @@ -1,9 +1,15 @@ https://github.com/ruby/ruby/blob/trunk/ChangeLog#L1 -Fri Jul 5 01:23:33 2013 Akinori MUSHA <knu@i...> +Fri Jul 5 02:14:00 2013 Akinori MUSHA <knu@i...> * lib/fileutils.rb (FileUtils#symbolic_modes_to_i): Fix the wrong character class [+-=], which happened to match all desired characters but also match undesired characters. + * lib/fileutils.rb (FileUtils.chmod{,_R}): Enhance the symbolic + mode parser to support the permission symbols u/g/o and multiple + actions as defined in SUS, so that chmod("g=o+w", file) works as + expected. Invalid symbolic modes are now rejected with + ArgumentError. + Fri Jul 5 00:25:39 2013 Nobuyoshi Nakada <nobu@r...> * lib/mkmf.rb (have_framework): allow header file to check. Index: lib/fileutils.rb =================================================================== --- lib/fileutils.rb (revision 41781) +++ lib/fileutils.rb (revision 41782) @@ -868,62 +868,78 @@ module FileUtils https://github.com/ruby/ruby/blob/trunk/lib/fileutils.rb#L868 OPT_TABLE['install'] = [:mode, :preserve, :noop, :verbose] def user_mask(target) #:nodoc: - mask = 0 - target.each_byte do |byte_chr| - case byte_chr.chr - when "u" - mask |= 04700 - when "g" - mask |= 02070 - when "o" - mask |= 01007 - when "a" - mask |= 07777 + target.each_char.inject(0) do |mask, chr| + case chr + when "u" + mask | 04700 + when "g" + mask | 02070 + when "o" + mask | 01007 + when "a" + mask | 07777 + else + raise ArgumentError, "invalid `who' symbol in file mode: #{chr}" end end - mask end private_module_function :user_mask - def mode_mask(mode, path) #:nodoc: - mask = 0 - mode.each_byte do |byte_chr| - case byte_chr.chr - when "r" - mask |= 0444 - when "w" - mask |= 0222 - when "x" - mask |= 0111 - when "X" - mask |= 0111 if FileTest::directory? path - when "s" - mask |= 06000 - when "t" - mask |= 01000 - end + def apply_mask(mode, user_mask, op, mode_mask) + case op + when '=' + (mode & ~user_mask) | (user_mask & mode_mask) + when '+' + mode | (user_mask & mode_mask) + when '-' + mode & ~(user_mask & mode_mask) end - mask end - private_module_function :mode_mask + private_module_function :apply_mask - def symbolic_modes_to_i(modes, path) #:nodoc: - current_mode = (File.stat(path).mode & 07777) - modes.split(/,/).inject(0) do |mode, mode_sym| - mode_sym = "a#{mode_sym}" if mode_sym =~ %r!^[=+-]! - target, mode = mode_sym.split %r![=+-]! + def symbolic_modes_to_i(mode_sym, path) #:nodoc: + mode_sym.split(/,/).inject(File.stat(path).mode & 07777) do |current_mode, clause| + target, *actions = clause.split(/([=+-])/) + raise ArgumentError, "invalid file mode: #{mode_sym}" if actions.empty? + target = 'a' if target.empty? user_mask = user_mask(target) - mode_mask = mode_mask(mode ? mode : "", path) + actions.each_slice(2) do |op, perm| + need_apply = op == '=' + mode_mask = (perm || '').each_char.inject(0) do |mask, chr| + case chr + when "r" + mask | 0444 + when "w" + mask | 0222 + when "x" + mask | 0111 + when "X" + if FileTest.directory? path + mask | 0111 + else + mask + end + when "s" + mask | 06000 + when "t" + mask | 01000 + when "u", "g", "o" + if mask.nonzero? + current_mode = apply_mask(current_mode, user_mask, op, mask) + end + need_apply = false + copy_mask = user_mask(chr) + (current_mode & copy_mask) / (copy_mask & 0111) * (user_mask & 0111) + else + raise ArgumentError, "invalid `perm' symbol in file mode: #{chr}" + end + end - case mode_sym - when /=/ - current_mode &= ~(user_mask) - current_mode |= user_mask & mode_mask - when /\+/ - current_mode |= user_mask & mode_mask - when /-/ - current_mode &= ~(user_mask & mode_mask) + if mode_mask.nonzero? || need_apply + current_mode = apply_mask(current_mode, user_mask, op, mode_mask) + end end + current_mode end end private_module_function :symbolic_modes_to_i Index: test/fileutils/test_fileutils.rb =================================================================== --- test/fileutils/test_fileutils.rb (revision 41781) +++ test/fileutils/test_fileutils.rb (revision 41782) @@ -932,6 +932,12 @@ class TestFileUtils https://github.com/ruby/ruby/blob/trunk/test/fileutils/test_fileutils.rb#L932 check_singleton :chmod touch 'tmp/a' + chmod "u=wrx,g=rx,o=x", 'tmp/a' + assert_equal 0751, File.stat('tmp/a').mode & 07777 + chmod "g+w-x", 'tmp/a' + assert_equal 0761, File.stat('tmp/a').mode & 07777 + chmod "o+r,g=o+w,o-r,u-o", 'tmp/a' # 761 => 763 => 773 => 771 => 671 + assert_equal 0671, File.stat('tmp/a').mode & 07777 chmod "u=wrx,g=,o=", 'tmp/a' assert_equal 0700, File.stat('tmp/a').mode & 0777 chmod "u=rx,go=", 'tmp/a' @@ -956,6 +962,26 @@ class TestFileUtils https://github.com/ruby/ruby/blob/trunk/test/fileutils/test_fileutils.rb#L962 assert_equal 0500, File.stat('tmp/a').mode & 07777 end + assert_raises_with_message(ArgumentError, /invalid\b.*\bfile mode/) { + chmod "a", 'tmp/a' + } + + assert_raises_with_message(ArgumentError, /invalid\b.*\bfile mode/) { + chmod "x+a", 'tmp/a' + } + + assert_raises_with_message(ArgumentError, /invalid\b.*\bfile mode/) { + chmod "u+z", 'tmp/a' + } + + assert_raises_with_message(ArgumentError, /invalid\b.*\bfile mode/) { + chmod ",+x", 'tmp/a' + } + + assert_raises_with_message(ArgumentError, /invalid\b.*\bfile mode/) { + chmod "755", 'tmp/a' + } + end if have_file_perm? -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/