From 57873ba60f3619c38fede8f6e3c73c9660d0c4c8 Mon Sep 17 00:00:00 2001 From: aamine Date: Sat, 21 May 2005 17:40:24 +0000 Subject: * lib/fileutils.rb (rm_r): new option :secure (default = true). * lib/fileutils.rb (remove_file, remove_dir): try chmod(700) only on Windows. * lib/fileutils.rb: does not depend on find.rb. * lib/fileutils.rb: new method chmod_R. * lib/fileutils.rb (chown_R): did not work. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@8494 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ChangeLog | 13 +++ lib/fileutils.rb | 284 ++++++++++++++++++++++++++++++++++++++----------------- 2 files changed, 208 insertions(+), 89 deletions(-) diff --git a/ChangeLog b/ChangeLog index 42808f9c70..bb879b6622 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,16 @@ +Sun May 22 02:39:57 2005 Minero Aoki + + * lib/fileutils.rb (rm_r): new option :secure (default = true). + + * lib/fileutils.rb (remove_file, remove_dir): try chmod(700) only + on Windows. + + * lib/fileutils.rb: does not depend on find.rb. + + * lib/fileutils.rb: new method chmod_R. + + * lib/fileutils.rb (chown_R): did not work. + Sat May 21 10:23:21 2005 Hirokazu Yamamoto * bcc32/Makefile.sub: tds files were not deleted when DESTDIR diff --git a/lib/fileutils.rb b/lib/fileutils.rb index 60a5456e77..7c5307ff5c 100644 --- a/lib/fileutils.rb +++ b/lib/fileutils.rb @@ -75,18 +75,13 @@ # :verbose flags to methods in FileUtils. # -require 'find' - -unless defined?(Errno::EXDEV) - module Errno - class EXDEV < SystemCallError; end - end -end - module FileUtils # All methods are module_function. + # This hash table holds command options. + OPT_TABLE = {} #:nodoc: internal use only + # # Options: (none) # @@ -117,6 +112,9 @@ module FileUtils alias chdir cd + OPT_TABLE['cd'] = + OPT_TABLE['chdir'] = %w( noop verbose ) + # # Options: (none) # @@ -160,6 +158,8 @@ module FileUtils end end + OPT_TABLE['mkdir'] = %w( noop verbose mode ) + # # Options: mode noop verbose # @@ -211,6 +211,10 @@ module FileUtils alias mkpath mkdir_p alias makedirs mkdir_p + OPT_TABLE['mkdir_p'] = + OPT_TABLE['mkpath'] = + OPT_TABLE['makedirs'] = %w( noop verbose ) + def fu_mkdir(path, mode) path = path.sub(%r, '') if mode @@ -243,6 +247,8 @@ module FileUtils end end + OPT_TABLE['rmdir'] = %w( noop verbose ) + # # Options: force noop verbose # @@ -278,6 +284,9 @@ module FileUtils alias link ln + OPT_TABLE['ln'] = + OPT_TABLE['link'] = %w( noop verbose force ) + # # Options: force noop verbose # @@ -313,6 +322,9 @@ module FileUtils alias symlink ln_s + OPT_TABLE['ln_s'] = + OPT_TABLE['symlink'] = %w( noop verbose force ) + # # Options: noop verbose # @@ -326,6 +338,8 @@ module FileUtils ln_s src, dest, options end + OPT_TABLE['ln_sf'] = %w( noop verbose ) + # # Options: preserve noop verbose # @@ -350,6 +364,9 @@ module FileUtils alias copy cp + OPT_TABLE['cp'] = + OPT_TABLE['copy'] = %w( noop verbose preserve ) + # # Options: preserve noop verbose # @@ -380,9 +397,9 @@ module FileUtils fu_each_src_dest(src, dest) do |s,d| if File.directory?(s) - fu_traverse(s) {|rel, deref, st| - ctx = CopyContext_.new(options[:preserve], deref, st) - ctx.copy_entry "#{s}/#{rel}", "#{d}/#{rel}" + fu_traverse(s) {|rel, deref, st, lst| + ctx = CopyContext_.new(options[:preserve], deref, st, lst) + ctx.copy_entry fu_join(s, rel), fu_join(d, rel) } else copy_file s, d, options[:preserve] @@ -390,21 +407,7 @@ module FileUtils end end - def fu_traverse(prefix, dereference_root = true) #:nodoc: - stack = ['.'] - deref = dereference_root - while rel = stack.pop - st = File.lstat("#{prefix}/#{rel}") - if st.directory? and (deref or not st.symlink?) - stack.concat Dir.entries("#{prefix}/#{rel}")\ - .reject {|ent| ent == '.' or ent == '..' }\ - .map {|ent| "#{rel}/#{ent.untaint}" }.reverse - end - yield rel, deref, st - deref = false - end - end - private :fu_traverse + OPT_TABLE['cp_r'] = %w( noop verbose preserve ) # # Copies a file system entry +src+ to +dest+. @@ -440,6 +443,7 @@ module FileUtils end def fu_copy_stream0(src, dest, blksize) #:nodoc: + # FIXME: readpartial? while s = src.read(blksize) dest.write s end @@ -449,10 +453,11 @@ module FileUtils class CopyContext_ # :nodoc: internal use only include ::FileUtils - def initialize(preserve = false, dereference = false, stat = nil) + def initialize(preserve = false, dereference = false, stat = nil, lstat = nil) @preserve = preserve @dereference = dereference @stat = stat + @lstat = lstat end def copy_entry(src, dest) @@ -529,7 +534,7 @@ module FileUtils if @dereference @stat ||= ::File.stat(path) else - @stat ||= ::File.lstat(path) + @lstat ||= ::File.lstat(path) end end @@ -600,6 +605,9 @@ module FileUtils alias move mv + OPT_TABLE['mv'] = + OPT_TABLE['move'] = %w( noop verbose force ) + def fu_stat(path) File.stat(path) rescue SystemCallError @@ -635,13 +643,16 @@ module FileUtils fu_output_message "rm#{options[:force] ? ' -f' : ''} #{list.join ' '}" if options[:verbose] return if options[:noop] - list.each do |fname| - remove_file fname, options[:force] + list.each do |path| + remove_file path, options[:force] end end alias remove rm + OPT_TABLE['rm'] = + OPT_TABLE['remove'] = %w( noop verbose force ) + # # Options: noop verbose # @@ -657,8 +668,11 @@ module FileUtils alias safe_unlink rm_f + OPT_TABLE['rm_f'] = + OPT_TABLE['safe_unlink'] = %w( noop verbose ) + # - # Options: force noop verbose + # Options: force noop verbose secure # # remove files +list+[0] +list+[1]... If +list+[n] is a directory, # removes its all contents recursively. This method ignores @@ -666,35 +680,61 @@ module FileUtils # # FileUtils.rm_r Dir.glob('/tmp/*') # FileUtils.rm_r '/', :force => true # :-) + # + # When :secure options is set, this method chmod(700) all directories + # under +list+[n] at first. This option is required to avoid + # time-to-check-to-time-to-use security problem. Default is :secure=>true. # def rm_r(list, options = {}) - fu_check_options options, :force, :noop, :verbose + fu_check_options options, :force, :noop, :verbose, :secure + options[:secure] = true unless options.key?(:secure) list = fu_list(list) fu_output_message "rm -r#{options[:force] ? 'f' : ''} #{list.join ' '}" if options[:verbose] return if options[:noop] - list.each do |fname| + list.each do |path| begin - st = File.lstat(fname) + st = File.lstat(path) rescue next if options[:force] raise end - if st.symlink? then remove_file fname, options[:force] - elsif st.directory? then remove_dir fname, options[:force] - else remove_file fname, options[:force] + if st.symlink? + remove_file path, options[:force] + elsif st.directory? + begin + fu_clear_permission path if options[:secure] + rescue + end + remove_dir path, options[:force] + else + remove_file path, options[:force] + end + end + end + + OPT_TABLE['rm_r'] = %w( noop verbose force ) + + def fu_clear_permission(prefix) + fu_find([prefix]) do |path, lstat| + if lstat.directory? + begin + File.chmod 0700, path + rescue Errno::EPERM + end end end end + private :fu_clear_permission # - # Options: noop verbose + # Options: noop verbose secure # # Same as # #rm_r(list, :force => true) # def rm_rf(list, options = {}) - fu_check_options options, :noop, :verbose + fu_check_options options, :noop, :verbose, :secure options = options.dup options[:force] = true rm_r list, options @@ -702,6 +742,9 @@ module FileUtils alias rmtree rm_rf + OPT_TABLE['rm_rf'] = + OPT_TABLE['rmtree'] = %w( noop verbose ) + # Removes a file +path+. # This method ignores StandardError if +force+ is true. def remove_file(path, force = false) @@ -711,10 +754,10 @@ module FileUtils rescue Errno::ENOENT raise unless force rescue => err - if first_time_p + if windows? and first_time_p first_time_p = false begin - File.chmod 0777, path + File.chmod 0700, path retry rescue SystemCallError end @@ -743,10 +786,10 @@ module FileUtils rescue Errno::ENOENT raise unless force rescue => err - if first_time_p + if windows? and first_time_p first_time_p = false begin - File.chmod 0777, dir + File.chmod 0700, dir retry rescue SystemCallError end @@ -816,6 +859,8 @@ module FileUtils end end + OPT_TABLE['install'] = %w( noop verbose preserve mode ) + # # Options: noop verbose # @@ -834,6 +879,46 @@ module FileUtils File.chmod mode, *list end + OPT_TABLE['chmod'] = %w( noop verbose ) + + # + # Options: noop verbose force + # + # Changes permission bits on the named files (in +list+) + # to the bit pattern represented by +mode+. + # + # FileUtils.chmod_R 0700, "/tmp/app.#{$$}" + # + def chmod_R(mode, list, options = {}) + lchmod_avail = nil + lchmod_checked = false + fu_check_options options, :noop, :verbose, :force + list = fu_list(list) + fu_output_message sprintf('chmod -R%s %o %s', + (options[:force] ? 'f' : ''), + mode, list.join(' ')) if options[:verbose] + return if options[:noop] + fu_find(list) do |path, lst| + begin + if lst.symlink? + begin + File.lchmod mode, path if lchmod_avail or not lchmod_checked + lchmod_avail = true + rescue NotImplementedError + lchmod_avail = false + end + lchmod_checked = true + else + File.chmod mode, path + end + rescue + raise unless options[:force] + end + end + end + + OPT_TABLE['chmod_R'] = %w( noop verbose ) + # # Options: noop verbose # @@ -856,8 +941,10 @@ module FileUtils File.chown fu_get_uid(user), fu_get_gid(group), *list end + OPT_TABLE['chown'] = %w( noop verbose ) + # - # Options: noop verbose + # Options: noop verbose force # # Changes owner and group on the named files (in +list+) # to the user +user+ and the group +group+ recursively. @@ -868,23 +955,40 @@ module FileUtils # FileUtils.chown_R 'www', 'www', '/var/www/htdocs' # FileUtils.chown_R 'cvs', 'cvs', '/var/cvs', :verbose => true # - def chown_R(mode, list, options = {}) - fu_check_options options, :noop, :verbose + def chown_R(user, group, list, options = {}) + lchown_avail = nil + lchown_checked = false + fu_check_options options, :noop, :verbose, :force list = fu_list(list) - fu_output_message sprintf('chown -R %s%s', + fu_output_message sprintf('chown -R%s %s%s', + (options[:force] ? 'f' : ''), [user,group].compact.join(':') + ' ', list.join(' ')) if options[:verbose] return if options[:noop] uid = fu_get_uid(user) gid = fu_get_gid(group) return unless uid or gid - list.each do |prefix| - Find.find(prefix) do |path| - File.chown uid, gid, path + fu_find(list) do |path, lst| + begin + if lst.symlink? + begin + File.lchown uid, gid, path if lchown_avail or not lchown_checked + lchown_avail = true + rescue NotImplementedError + lchown_avail = false + end + lchown_checked = true + else + File.chown uid, gid, path + end + rescue + raise unless options[:force] end end end + OPT_TABLE['chown_R'] = %w( noop verbose ) + begin require 'etc' @@ -935,19 +1039,51 @@ module FileUtils return if options[:noop] t = Time.now - list.each do |fname| + list.each do |path| begin - File.utime(t, t, fname) + File.utime(t, t, path) rescue Errno::ENOENT - File.open(fname, 'a') { + File.open(path, 'a') { ; } end end end + OPT_TABLE['touch'] = %w( noop verbose ) + private + def fu_find(roots) #:nodoc: + roots.each do |prefix| + fu_traverse(prefix, false) do |rel, deref, stat, lstat| + yield fu_join(prefix, rel), lstat + end + end + end + + def fu_traverse(prefix, dereference_root = true) #:nodoc: + stack = ['.'] + deref = dereference_root + while rel = stack.pop + lst = File.lstat(fu_join(prefix, rel)) + st = ((lst.symlink?() ? File.stat(fu_join(prefix, rel)) : lst) rescue nil) + yield rel, deref, st, lst + if (st and st.directory?) and (deref or not lst.symlink?) + stack.concat Dir.entries(fu_join(prefix, rel))\ + .reject {|ent| ent == '.' or ent == '..' }\ + .map {|ent| fu_join(rel, ent.untaint) }.reverse + end + deref = false + end + end + + def fu_join(dir, base) + return File.path(dir) if base == '.' + return File.path(base) if dir == '.' + File.join(dir, base) + end + def fu_check_options(options, *optdecl) h = options.dup optdecl.each do |name| @@ -996,7 +1132,11 @@ module FileUtils end def have_st_ino? - /mswin|mingw|bccwin|wince|emx/ !~ RUBY_PLATFORM + not windows? + end + + def windows? + /mswin|mingw|bccwin|wince|emx/ =~ RUBY_PLATFORM end def fu_stream_blksize(*streams) @@ -1037,42 +1177,9 @@ module FileUtils args end - + # All Methods are public instance method and are public class method. extend self - - OPT_TABLE = { - 'cd' => %w( noop verbose ), - 'chdir' => %w( noop verbose ), - 'chmod' => %w( noop verbose ), - 'chown' => %w( noop verbose ), - 'chown_R' => %w( noop verbose ), - 'copy' => %w( noop verbose preserve ), - 'cp' => %w( noop verbose preserve ), - 'cp_r' => %w( noop verbose preserve ), - 'install' => %w( noop verbose preserve mode ), - 'link' => %w( noop verbose force ), - 'ln' => %w( noop verbose force ), - 'ln_s' => %w( noop verbose force ), - 'ln_sf' => %w( noop verbose ), - 'makedirs' => %w( noop verbose ), - 'mkdir' => %w( noop verbose mode ), - 'mkdir_p' => %w( noop verbose mode ), - 'mkpath' => %w( noop verbose ), - 'move' => %w( noop verbose force ), - 'mv' => %w( noop verbose force ), - 'remove' => %w( noop verbose force ), - 'rm' => %w( noop verbose force ), - 'rm_f' => %w( noop verbose ), - 'rm_r' => %w( noop verbose force ), - 'rm_rf' => %w( noop verbose ), - 'rmtree' => %w( noop verbose ), - 'rmdir' => %w( noop verbose ), - 'safe_unlink' => %w( noop verbose ), - 'symlink' => %w( noop verbose force ), - 'touch' => %w( noop verbose ) - } - # # Returns an Array of method names which have any options. # @@ -1121,7 +1228,6 @@ module FileUtils OPT_TABLE.keys.select {|m| OPT_TABLE[m].include?(opt) } end - # # This module has all methods of FileUtils module, but it outputs messages # before acting. This equates to passing the :verbose flag to -- cgit v1.2.3