модуль Bundler::FileUtils
fileutils.rb
Авторское право © 2000-2007 Minero Aoki
Данная программа является свободной программной. Вы можете распространять/модифицировать данную программу в соответствии с условиями ruby.
модуль Bundler::FileUtils
Пространство имен для нескольких методов работы с файлами: копирование, перемещение, удаление и т.д.
Module Функции
require 'bundler/vendor/fileutils/lib/fileutils'
Bundler::FileUtils.cd(dir, options)
Bundler::FileUtils.cd(dir, options) {|dir| block }
Bundler::FileUtils.pwd()
Bundler::FileUtils.mkdir(dir, options)
Bundler::FileUtils.mkdir(list, options)
Bundler::FileUtils.mkdir_p(dir, options)
Bundler::FileUtils.mkdir_p(list, options)
Bundler::FileUtils.rmdir(dir, options)
Bundler::FileUtils.rmdir(list, options)
Bundler::FileUtils.ln(target, link, options)
Bundler::FileUtils.ln(targets, dir, options)
Bundler::FileUtils.ln_s(target, link, options)
Bundler::FileUtils.ln_s(targets, dir, options)
Bundler::FileUtils.ln_sf(target, link, options)
Bundler::FileUtils.cp(src, dest, options)
Bundler::FileUtils.cp(list, dir, options)
Bundler::FileUtils.cp_r(src, dest, options)
Bundler::FileUtils.cp_r(list, dir, options)
Bundler::FileUtils.mv(src, dest, options)
Bundler::FileUtils.mv(list, dir, options)
Bundler::FileUtils.rm(list, options)
Bundler::FileUtils.rm_r(list, options)
Bundler::FileUtils.rm_rf(list, options)
Bundler::FileUtils.install(src, dest, options)
Bundler::FileUtils.chmod(mode, list, options)
Bundler::FileUtils.chmod_R(mode, list, options)
Bundler::FileUtils.chown(user, group, list, options)
Bundler::FileUtils.chown_R(user, group, list, options)
Bundler::FileUtils.touch(list, options)
Параметр options — это хэш опций, взятых из списка :force, :noop, :preserve, и :verbose. Значение :noop означает, что никаких изменений не вносится. Остальные три значения очевидны. Каждый метод документирует опции, которые он поддерживает.
Все методы, которые работают с файлами или каталогами в качестве «источника», могут принимать либо один файл, либо список файлов в качестве аргумента. Смотрите документацию метода для примеров.
Существуют некоторые методы «низкого уровня», которые не принимают никаких опций:
Bundler::FileUtils.copy_entry(src, dest, preserve = false, dereference = false) Bundler::FileUtils.copy_file(src, dest, preserve = false, dereference = true) Bundler::FileUtils.copy_stream(srcstream, deststream) Bundler::FileUtils.remove_entry(path, force = false) Bundler::FileUtils.remove_entry_secure(path, force = false) Bundler::FileUtils.remove_file(path, force = false) Bundler::FileUtils.compare_file(path_a, path_b) Bundler::FileUtils.compare_stream(stream_a, stream_b) Bundler::FileUtils.uptodate?(file, cmp_list)
модуль Bundler::FileUtils::Verbose
Этот модуль содержит все методы модуля Bundler::FileUtils, но выводит сообщения перед выполнением действий. Это эквивалентно передаче флага :verbose методам в модуле Bundler::FileUtils.
модуль Bundler::FileUtils::NoWrite
Этот модуль содержит все методы модуля Bundler::FileUtils, но никогда не изменяет файлы/каталоги. Это эквивалентно передаче флага :noop методам в модуле Bundler::FileUtils.
модуль Bundler::FileUtils::DryRun
Этот модуль содержит все методы модуля Bundler::FileUtils, но никогда не изменяет файлы/каталоги. Это эквивалентно передаче флагов :noop и :verbose методам в модуле Bundler::FileUtils.
Константы
- LOW_METHODS
- METHODS
Методы публичного класса
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 118
def cd(dir, verbose: nil, &block) # :yield: dir
fu_output_message "cd #{dir}" if verbose
Dir.chdir(dir, &block)
fu_output_message 'cd -' if verbose and block
end Изменяет текущую директорию на директорию dir.
Если этот метод вызывается с блоком, он возвращается к старой рабочей директории после завершения выполнения блока.
Bundler::FileUtils.cd('/', :verbose => true) # chdir and report it
Bundler::FileUtils.cd('/') do # chdir
# ... # do something
end # return to original directory
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 914
def chmod(mode, list, noop: nil, verbose: nil)
list = fu_list(list)
fu_output_message sprintf('chmod %s %s', mode_to_s(mode), list.join(' ')) if verbose
return if noop
list.each do |path|
Entry_.new(path).chmod(fu_mode(mode, path))
end
end Изменяет биты разрешений на указанные файлы (в list) на битовую комбинацию, представленную mode.
mode используются символические и абсолютные режимы.
Абсолютный режим —
Bundler::FileUtils.chmod 0755, 'somecommand' Bundler::FileUtils.chmod 0644, %w(my.rb your.rb his.rb her.rb) Bundler::FileUtils.chmod 0755, '/usr/bin/ruby', :verbose => true
Символический режим —
Bundler::FileUtils.chmod "u=wrx,go=rx", 'somecommand' Bundler::FileUtils.chmod "u=wr,go=rr", %w(my.rb your.rb his.rb her.rb) Bundler::FileUtils.chmod "u=wrx,go=rx", '/usr/bin/ruby', :verbose => true
- “a”
-
— это маска для пользователя, группы и других.
- “u”
-
— это маска пользователя.
- “g”
-
— это маска группы.
- “o”
-
— это маска других.
- “w”
-
— это разрешение на запись.
- “r”
-
— это разрешение на чтение.
- “x”
-
— это разрешение на выполнение.
- “X”
-
— это разрешение на выполнение только для каталогов, должно использоваться в сочетании с “+”
- “s”
-
— это uid, gid.
- “t”
-
— это бит «sticky».
- “+”
-
— добавляется к классу с указанным режимом.
- “-”
-
— удаляется из класса с указанным режимом.
- “=”
-
— точное значение класса будет задано с указанным режимом.
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 931
def chmod_R(mode, list, noop: nil, verbose: nil, force: nil)
list = fu_list(list)
fu_output_message sprintf('chmod -R%s %s %s',
(force ? 'f' : ''),
mode_to_s(mode), list.join(' ')) if verbose
return if noop
list.each do |root|
Entry_.new(root).traverse do |ent|
begin
ent.chmod(fu_mode(mode, ent.path))
rescue
raise unless force
end
end
end
end Изменяет биты разрешений на указанные файлы (в list) на битовую комбинацию, представленную mode.
Bundler::FileUtils.chmod_R 0700, "/tmp/app.#{$$}"
Bundler::FileUtils.chmod_R "u=wrx", "/tmp/app.#{$$}"
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 959
def chown(user, group, list, noop: nil, verbose: nil)
list = fu_list(list)
fu_output_message sprintf('chown %s %s',
(group ? "#{user}:#{group}" : user || ':'),
list.join(' ')) if verbose
return if noop
uid = fu_get_uid(user)
gid = fu_get_gid(group)
list.each do |path|
Entry_.new(path).chown uid, gid
end
end Изменяет владельца и группу на указанных файлах (в list) на пользователя user и группу group. user и group могут быть идентификатором (целое число/строка) или именем (строка). Если user или group равны null, этот метод не изменяет атрибут.
Bundler::FileUtils.chown 'root', 'staff', '/usr/local/bin/ruby'
Bundler::FileUtils.chown nil, 'bin', Dir.glob('/usr/bin/*'), :verbose => true
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 983
def chown_R(user, group, list, noop: nil, verbose: nil, force: nil)
list = fu_list(list)
fu_output_message sprintf('chown -R%s %s %s',
(force ? 'f' : ''),
(group ? "#{user}:#{group}" : user || ':'),
list.join(' ')) if verbose
return if noop
uid = fu_get_uid(user)
gid = fu_get_gid(group)
list.each do |root|
Entry_.new(root).traverse do |ent|
begin
ent.chown uid, gid
rescue
raise unless force
end
end
end
end Рекурсивно изменяет владельца и группу на указанных файлах (в list) на пользователя user и группу group. user и group могут быть идентификатором (целое число/строка) или именем (строка). Если user или group равны null, этот метод не изменяет атрибут.
Bundler::FileUtils.chown_R 'www', 'www', '/var/www/htdocs' Bundler::FileUtils.chown_R 'cvs', 'cvs', '/var/cvs', :verbose => true
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 725
def compare_file(a, b)
return false unless File.size(a) == File.size(b)
File.open(a, 'rb') {|fa|
File.open(b, 'rb') {|fb|
return compare_stream(fa, fb)
}
}
end Возвращает true, если содержимое файла a и файла b идентичны.
Bundler::FileUtils.compare_file('somefile', 'somefile') #=> true
Bundler::FileUtils.compare_file('/dev/null', '/dev/urandom') #=> false
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 743
def compare_stream(a, b)
bsize = fu_stream_blksize(a, b)
sa = String.new(capacity: bsize)
sb = String.new(capacity: bsize)
begin
a.read(bsize, sa)
b.read(bsize, sb)
return true if sa.empty? && sb.empty?
end while sa == sb
false
end Возвращает true, если содержимое потока a и b идентичны.
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 412
def copy_entry(src, dest, preserve = false, dereference_root = false, remove_destination = false)
Entry_.new(src, nil, dereference_root).wrap_traverse(proc do |ent|
destent = Entry_.new(dest, ent.rel, false)
File.unlink destent.path if remove_destination && File.file?(destent.path)
ent.copy destent.path
end, proc do |ent|
destent = Entry_.new(dest, ent.rel, false)
ent.copy_metadata destent.path if preserve
end)
end Копирует системный элемент src в dest. Если src является каталогом, этот метод рекурсивно копирует его содержимое. Этот метод сохраняет типы файлов, например, символические ссылки, каталоги… (FIFO, файлы устройств и т. д. пока не поддерживаются)
Оба src и dest должны быть именами путей. src должен существовать, dest не должен существовать.
Если preserve равно true, этот метод сохраняет владельца, группу и время последнего изменения. Разрешения копируются независимо от preserve.
Если dereference_root равно true, этот метод дескриминирует корень дерева.
Если remove_destination равно true, этот метод удаляет каждый целевой файл перед копированием.
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 428 def copy_file(src, dest, preserve = false, dereference = true) ent = Entry_.new(src, nil, dereference) ent.copy_file dest ent.copy_metadata dest if preserve end
Копирует содержимое файла src в dest. Оба src и dest должны быть именами путей.
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 440 def copy_stream(src, dest) IO.copy_stream(src, dest) end
Копирует поток src в dest. src должен отвечать на read(n), а dest должен отвечать на write(str).
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 353
def cp(src, dest, preserve: nil, noop: nil, verbose: nil)
fu_output_message "cp#{preserve ? ' -p' : ''} #{[src,dest].flatten.join ' '}" if verbose
return if noop
fu_each_src_dest(src, dest) do |s, d|
copy_file s, d, preserve
end
end Копирует содержимое файла src в dest. Если dest является каталогом, копирует src в dest/src.
Если src является списком файлов, то dest должен быть каталогом.
Bundler::FileUtils.cp 'eval.c', 'eval.c.org' Bundler::FileUtils.cp %w(cgi.rb complex.rb date.rb), '/usr/lib/ruby/1.6' Bundler::FileUtils.cp %w(cgi.rb complex.rb date.rb), '/usr/lib/ruby/1.6', :verbose => true Bundler::FileUtils.cp 'symlink', 'dest' # copy content, "dest" is not a symlink
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 386
def cp_r(src, dest, preserve: nil, noop: nil, verbose: nil,
dereference_root: true, remove_destination: nil)
fu_output_message "cp -r#{preserve ? 'p' : ''}#{remove_destination ? ' --remove-destination' : ''} #{[src,dest].flatten.join ' '}" if verbose
return if noop
fu_each_src_dest(src, dest) do |s, d|
copy_entry s, d, preserve, dereference_root, remove_destination
end
end Копирует src в dest. Если src является каталогом, этот метод копирует всё его содержимое рекурсивно. Если dest является каталогом, копирует src в dest/src.
src может быть списком файлов.
# Installing Ruby library "mylib" under the site_ruby
Bundler::FileUtils.rm_r site_ruby + '/mylib', :force
Bundler::FileUtils.cp_r 'lib/', site_ruby + '/mylib'
# Examples of copying several files to target directory.
Bundler::FileUtils.cp_r %w(mail.rb field.rb debug/), site_ruby + '/tmail'
Bundler::FileUtils.cp_r Dir.glob('*.rb'), '/home/foo/lib/ruby', :noop => true, :verbose => true
# If you want to copy all contents of a directory instead of the
# directory itself, c.f. src/x -> dest/x, src/y -> dest/y,
# use following code.
Bundler::FileUtils.cp_r 'src/.', 'dest' # cp_r('src', 'dest') makes dest/src,
# but this doesn't.
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 764
def install(src, dest, mode: nil, owner: nil, group: nil, preserve: nil,
noop: nil, verbose: nil)
if verbose
msg = +"install -c"
msg << ' -p' if preserve
msg << ' -m ' << mode_to_s(mode) if mode
msg << " -o #{owner}" if owner
msg << " -g #{group}" if group
msg << ' ' << [src,dest].flatten.join(' ')
fu_output_message msg
end
return if noop
uid = fu_get_uid(owner)
gid = fu_get_gid(group)
fu_each_src_dest(src, dest) do |s, d|
st = File.stat(s)
unless File.exist?(d) and compare_file(s, d)
remove_file d, true
copy_file s, d
File.utime st.atime, st.mtime, d if preserve
File.chmod fu_mode(mode, st), d if mode
File.chown uid, gid, d if uid or gid
end
end
end Если src не совпадает с dest, копирует его и изменяет режим разрешений на mode. Если dest является каталогом, результатом является dest/src. Этот метод удаляет цель перед копированием.
Bundler::FileUtils.install 'ruby', '/usr/local/bin/ruby', :mode => 0755, :verbose => true Bundler::FileUtils.install 'lib.rb', '/usr/local/lib/ruby/site_ruby', :verbose => true
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 283
def ln(src, dest, force: nil, noop: nil, verbose: nil)
fu_output_message "ln#{force ? ' -f' : ''} #{[src,dest].flatten.join ' '}" if verbose
return if noop
fu_each_src_dest0(src, dest) do |s,d|
remove_file d, true if force
File.link s, d
end
end В первом варианте создаёт жёсткую ссылку link, которая указывает на target. Если link уже существует, возникает исключение Errno::EEXIST. Но если установлен параметр :force, link перезаписывается.
Bundler::FileUtils.ln 'gcc', 'cc', verbose: true Bundler::FileUtils.ln '/usr/bin/emacs21', '/usr/bin/emacs'
Во втором варианте создаётся ссылка dir/target , указывающая на target. В третьем варианте создаются несколько жёстких ссылок в каталоге dir, указывающих на каждый элемент в targets. Если dir не является каталогом, возникает исключение Errno::ENOTDIR.
Bundler::FileUtils.cd '/sbin' Bundler::FileUtils.ln %w(cp mv mkdir), '/bin' # Now /sbin/cp and /bin/cp are linked.
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 316
def ln_s(src, dest, force: nil, noop: nil, verbose: nil)
fu_output_message "ln -s#{force ? 'f' : ''} #{[src,dest].flatten.join ' '}" if verbose
return if noop
fu_each_src_dest0(src, dest) do |s,d|
remove_file d, true if force
File.symlink s, d
end
end В первом варианте создаёт символическую ссылку link, которая указывает на target. Если link уже существует, возникает исключение Errno::EEXIST. Но если установлен параметр :force, link перезаписывается.
Bundler::FileUtils.ln_s '/usr/bin/ruby', '/usr/local/bin/ruby' Bundler::FileUtils.ln_s 'verylongsourcefilename.c', 'c', force: true
Во втором варианте создаётся ссылка dir/target , указывающая на target. В третьем варианте создаются несколько символических ссылок в каталоге dir, указывающих на каждый элемент в targets. Если dir не является каталогом, возникает исключение Errno::ENOTDIR.
Bundler::FileUtils.ln_s Dir.glob('/bin/*.rb'), '/home/foo/bin'
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 337 def ln_sf(src, dest, noop: nil, verbose: nil) ln_s src, dest, force: true, noop: noop, verbose: verbose end
То же самое, что и
Bundler::FileUtils.ln_s(*args, force: true)
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 160
def mkdir(list, mode: nil, noop: nil, verbose: nil)
list = fu_list(list)
fu_output_message "mkdir #{mode ? ('-m %03o ' % mode) : ''}#{list.join ' '}" if verbose
return if noop
list.each do |dir|
fu_mkdir dir, mode
end
end Создаёт один или несколько каталогов.
Bundler::FileUtils.mkdir 'test' Bundler::FileUtils.mkdir %w( tmp data ) Bundler::FileUtils.mkdir 'notexist', :noop => true # Does not really create. Bundler::FileUtils.mkdir 'tmp', :mode => 0700
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 186
def mkdir_p(list, mode: nil, noop: nil, verbose: nil)
list = fu_list(list)
fu_output_message "mkdir -p #{mode ? ('-m %03o ' % mode) : ''}#{list.join ' '}" if verbose
return *list if noop
list.map {|path| remove_trailing_slash(path)}.each do |path|
# optimize for the most common case
begin
fu_mkdir path, mode
next
rescue SystemCallError
next if File.directory?(path)
end
stack = []
until path == stack.last # dirname("/")=="/", dirname("C:/")=="C:/"
stack.push path
path = File.dirname(path)
end
stack.pop # root directory should exist
stack.reverse_each do |dir|
begin
fu_mkdir dir, mode
rescue SystemCallError
raise unless File.directory?(dir)
end
end
end
return *list
end Создаёт каталог и все его родительские каталоги. Например,
Bundler::FileUtils.mkdir_p '/usr/local/lib/ruby'
приводит к созданию следующих каталогов, если они не существуют.
-
/usr
-
/usr/local
-
/usr/local/lib
-
/usr/local/lib/ruby
Вы можете передать несколько каталогов одновременно в списке.
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 455
def mv(src, dest, force: nil, noop: nil, verbose: nil, secure: nil)
fu_output_message "mv#{force ? ' -f' : ''} #{[src,dest].flatten.join ' '}" if verbose
return if noop
fu_each_src_dest(src, dest) do |s, d|
destent = Entry_.new(d, nil, true)
begin
if destent.exist?
if destent.directory?
raise Errno::EEXIST, d
else
destent.remove_file if rename_cannot_overwrite_file?
end
end
begin
File.rename s, d
rescue Errno::EXDEV
copy_entry s, d, true
if secure
remove_entry_secure s, force
else
remove_entry s, force
end
end
rescue SystemCallError
raise unless force
end
end
end Перемещает файл(ы) src в dest. Если file и dest находятся на разных разделах диска, файл копируется, а затем удаляется исходный файл.
Bundler::FileUtils.mv 'badname.rb', 'goodname.rb'
Bundler::FileUtils.mv 'stuff.rb', '/notexist/lib/ruby', :force => true # no error
Bundler::FileUtils.mv %w(junk.txt dust.txt), '/home/foo/.trash/'
Bundler::FileUtils.mv Dir.glob('test*.rb'), 'test', :noop => true, :verbose => true
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 98 def pwd Dir.pwd end
Возвращает имя текущего каталога.
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 714 def remove_dir(path, force = false) remove_entry path, force # FIXME?? check if it is a directory end
Удаляет каталог dir и его содержимое рекурсивно. Этот метод игнорирует StandardError, если force равно true.
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 686
def remove_entry(path, force = false)
Entry_.new(path).postorder_traverse do |ent|
begin
ent.remove
rescue
raise unless force
end
end
rescue
raise unless force
end Этот метод удаляет запись в файловой системе path. path может быть обычным файлом, каталогом или чем-то ещё. Если path является каталогом, удалите его рекурсивно.
См. также remove_entry_secure.
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 609
def remove_entry_secure(path, force = false)
unless fu_have_symlink?
remove_entry path, force
return
end
fullpath = File.expand_path(path)
st = File.lstat(fullpath)
unless st.directory?
File.unlink fullpath
return
end
# is a directory.
parent_st = File.stat(File.dirname(fullpath))
unless parent_st.world_writable?
remove_entry path, force
return
end
unless parent_st.sticky?
raise ArgumentError, "parent directory is world writable, Bundler::FileUtils#remove_entry_secure does not work; abort: #{path.inspect} (parent directory mode #{'%o' % parent_st.mode})"
end
# freeze tree root
euid = Process.euid
File.open(fullpath + '/.') {|f|
unless fu_stat_identical_entry?(st, f.stat)
# symlink (TOC-to-TOU attack?)
File.unlink fullpath
return
end
f.chown euid, -1
f.chmod 0700
unless fu_stat_identical_entry?(st, File.lstat(fullpath))
# TOC-to-TOU attack?
File.unlink fullpath
return
end
}
# ---- tree root is frozen ----
root = Entry_.new(path)
root.preorder_traverse do |ent|
if ent.directory?
ent.chown euid, -1
ent.chmod 0700
end
end
root.postorder_traverse do |ent|
begin
ent.remove
rescue
raise unless force
end
end
rescue
raise unless force
end Этот метод удаляет запись в файловой системе path. path должен быть обычным файлом, каталогом или чем-то ещё. Если path является каталогом, удалите его рекурсивно. Этот метод необходим для предотвращения местной проблемы безопасности TOCTTOU (время проверки — время использования) метода rm_r. rm_r создаёт дыру в безопасности, когда:
-
Родительский каталог имеет разрешение на запись для всех (включая /tmp).
-
Удаляемая древовидная структура каталогов включает каталог с разрешением на запись для всех.
-
В системе есть символические ссылки.
Для предотвращения этой дыры в безопасности этот метод применяет специальную предобработку. Если path является каталогом, этот метод использует chown(2) и chmod(2) для всех удаляемых каталогов. Это требует, чтобы текущий процесс был владельцем удаляемой всей древовидной структуры каталогов или имел права суперпользователя (root).
ПРЕДУПРЕЖДЕНИЕ: Вы должны убедиться, что ВСЕ родительские каталоги не могут быть перемещены другими недоверенными пользователями. Например, родительские каталоги не должны принадлежать недоверенным пользователям и не должны иметь разрешение на запись для всех, за исключением случаев, когда установлен бит «клейкости».
ПРЕДУПРЕЖДЕНИЕ: Только владелец удаляемой древовидной структуры каталогов или системный суперпользователь (root) должен вызывать этот метод. В противном случае этот метод не работает.
Для получения подробностей об этой уязвимости безопасности см. пример из Perl:
Для fileutils.rb эта уязвимость была сообщена в [ruby-dev:26100].
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 703 def remove_file(path, force = false) Entry_.new(path).remove_file rescue raise unless force end
Удаляет файл path. Этот метод игнорирует StandardError, если force равно true.
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 501
def rm(list, force: nil, noop: nil, verbose: nil)
list = fu_list(list)
fu_output_message "rm#{force ? ' -f' : ''} #{list.join ' '}" if verbose
return if noop
list.each do |path|
remove_file path, force
end
end Удаляет файлы, указанные в list. Данный метод не может удалять каталоги. Все исключения StandardError игнорируются, если установлен параметр :force.
Bundler::FileUtils.rm %w( junk.txt dust.txt )
Bundler::FileUtils.rm Dir.glob('*.so')
Bundler::FileUtils.rm 'NotExistFile', :force => true # never raises exception
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 520 def rm_f(list, noop: nil, verbose: nil) rm list, force: true, noop: noop, verbose: verbose end
Эквивалентно
Bundler::FileUtils.rm(list, :force => true)
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 547
def rm_r(list, force: nil, noop: nil, verbose: nil, secure: nil)
list = fu_list(list)
fu_output_message "rm -r#{force ? 'f' : ''} #{list.join ' '}" if verbose
return if noop
list.each do |path|
if secure
remove_entry_secure path, force
else
remove_entry path, force
end
end
end Удаляет файлы list[0] list[1]… Если list[n] является каталогом, удаляет всё его содержимое рекурсивно. Этот метод игнорирует StandardError, когда установлен параметр :force.
Bundler::FileUtils.rm_r Dir.glob('/tmp/*')
Bundler::FileUtils.rm_r 'some_dir', :force => true
ВНИМАНИЕ: Этот метод может привести к уязвимости, если один из родительских каталогов или удаляемая структура каталогов имеют права доступа «для всех» (включая /tmp, права которого составляют 1777), а текущий процесс имеет сильные привилегии, такие как суперпользователь Unix (root), и система имеет символическую ссылку. Для безопасного удаления внимательно прочитайте документацию remove_entry_secure, и установите параметр :secure в значение true. По умолчанию :secure=>false.
ПРИМЕЧАНИЕ: Этот метод вызывает remove_entry_secure, если установлен параметр :secure. См. также remove_entry_secure.
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 569 def rm_rf(list, noop: nil, verbose: nil, secure: nil) rm_r list, force: true, noop: noop, verbose: verbose, secure: secure end
Эквивалентно
Bundler::FileUtils.rm_r(list, :force => true)
ВНИМАНИЕ: Этот метод может привести к локальной уязвимости. Сначала прочитайте документацию rm_r.
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 243
def rmdir(list, parents: nil, noop: nil, verbose: nil)
list = fu_list(list)
fu_output_message "rmdir #{parents ? '-p ' : ''}#{list.join ' '}" if verbose
return if noop
list.each do |dir|
begin
Dir.rmdir(dir = remove_trailing_slash(dir))
if parents
until (parent = File.dirname(dir)) == '.' or parent == dir
dir = parent
Dir.rmdir(dir)
end
end
rescue Errno::ENOTEMPTY, Errno::EEXIST, Errno::ENOENT
end
end
end Удаляет один или несколько каталогов.
Bundler::FileUtils.rmdir 'somedir' Bundler::FileUtils.rmdir %w(somedir anydir otherdir) # Does not really remove directory; outputs message. Bundler::FileUtils.rmdir 'somedir', :verbose => true, :noop => true
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 1042
def touch(list, noop: nil, verbose: nil, mtime: nil, nocreate: nil)
list = fu_list(list)
t = mtime
if verbose
fu_output_message "touch #{nocreate ? '-c ' : ''}#{t ? t.strftime('-t %Y%m%d%H%M.%S ') : ''}#{list.join ' '}"
end
return if noop
list.each do |path|
created = nocreate
begin
File.utime(t, t, path)
rescue Errno::ENOENT
raise if created
File.open(path, 'a') {
;
}
created = true
retry if t
end
end
end Обновляет время изменения (mtime) и время доступа (atime) файлов в list. Файлы создаются, если они не существуют.
Bundler::FileUtils.touch 'timestamp'
Bundler::FileUtils.touch Dir.glob('*.c'); system 'make'
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 135
def uptodate?(new, old_list)
return false unless File.exist?(new)
new_time = File.mtime(new)
old_list.each do |old|
if File.exist?(old)
return false unless new_time > File.mtime(old)
end
end
true
end Возвращает true, если new новее всех old_list. Несуществующие файлы старше любого файла.
Bundler::FileUtils.uptodate?('hello.o', %w(hello.c hello.h)) or \
system 'make hello.o'
Методы класса (private)
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 1549
def self.collect_method(opt)
OPT_TABLE.keys.select {|m| OPT_TABLE[m].include?(opt) }
end Возвращает массив имён методов, которые имеют опцию opt.
p Bundler::FileUtils.collect_method(:preserve) #=> ["cp", "cp_r", "copy", "install"]
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 1510 def self.commands OPT_TABLE.keys end
Возвращает массив имён методов, которые имеют какие-либо опции.
p Bundler::FileUtils.commands #=> ["chmod", "cp", "cp_r", "install", ...]
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 1530
def self.have_option?(mid, opt)
li = OPT_TABLE[mid.to_s] or raise ArgumentError, "no such method: #{mid}"
li.include?(opt)
end Возвращает true, если метод mid имеет опцию opt.
p Bundler::FileUtils.have_option?(:cp, :noop) #=> true p Bundler::FileUtils.have_option?(:rm, :force) #=> true p Bundler::FileUtils.have_option?(:rm, :preserve) #=> false
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 1519
def self.options
OPT_TABLE.values.flatten.uniq.map {|sym| sym.to_s }
end Возвращает массив имён опций.
p Bundler::FileUtils.options #=> ["noop", "force", "verbose", "preserve", "mode"]
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 1540
def self.options_of(mid)
OPT_TABLE[mid.to_s].map {|sym| sym.to_s }
end Возвращает массив имён опций метода mid.
p Bundler::FileUtils.options_of(:rm) #=> ["noop", "verbose", "force"]
Методы частного экземпляра
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 118
def cd(dir, verbose: nil, &block) # :yield: dir
fu_output_message "cd #{dir}" if verbose
Dir.chdir(dir, &block)
fu_output_message 'cd -' if verbose and block
end Изменяет текущую директорию на директорию dir.
Если этот метод вызывается с блоком, возвращается к старой рабочей директории после завершения выполнения блока.
Bundler::FileUtils.cd('/', :verbose => true) # chdir and report it
Bundler::FileUtils.cd('/') do # chdir
# ... # do something
end # return to original directory
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 914
def chmod(mode, list, noop: nil, verbose: nil)
list = fu_list(list)
fu_output_message sprintf('chmod %s %s', mode_to_s(mode), list.join(' ')) if verbose
return if noop
list.each do |path|
Entry_.new(path).chmod(fu_mode(mode, path))
end
end Изменяет биты разрешений указанных файлов (в list) на битовую схему, представленную mode.
Можно использовать символический и абсолютный режим.
Абсолютный режим
Bundler::FileUtils.chmod 0755, 'somecommand' Bundler::FileUtils.chmod 0644, %w(my.rb your.rb his.rb her.rb) Bundler::FileUtils.chmod 0755, '/usr/bin/ruby', :verbose => true
Символический режим
Bundler::FileUtils.chmod "u=wrx,go=rx", 'somecommand' Bundler::FileUtils.chmod "u=wr,go=rr", %w(my.rb your.rb his.rb her.rb) Bundler::FileUtils.chmod "u=wrx,go=rx", '/usr/bin/ruby', :verbose => true
- “a”
-
маска пользователя, группы и других.
- “u”
-
маска пользователя.
- “g”
-
маска группы.
- “o”
-
маска других.
- “w”
-
разрешение на запись.
- “r”
-
разрешение на чтение.
- “x”
-
разрешение на выполнение.
- “X”
-
разрешение на выполнение только для каталогов, должно использоваться вместе с “+”
- “s”
-
uid, gid.
- “t”
-
разрешение "sticky bit".
- “+”
-
добавляется к классу, заданному указанным режимом.
- “-”
-
Удаляется из заданного класса в соответствии с заданным режимом.
- “=”
-
Точное значение класса будет задано указанным режимом.
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 931
def chmod_R(mode, list, noop: nil, verbose: nil, force: nil)
list = fu_list(list)
fu_output_message sprintf('chmod -R%s %s %s',
(force ? 'f' : ''),
mode_to_s(mode), list.join(' ')) if verbose
return if noop
list.each do |root|
Entry_.new(root).traverse do |ent|
begin
ent.chmod(fu_mode(mode, ent.path))
rescue
raise unless force
end
end
end
end Изменяет биты разрешений указанных файлов (в list) на битовую схему, представленную mode.
Bundler::FileUtils.chmod_R 0700, "/tmp/app.#{$$}"
Bundler::FileUtils.chmod_R "u=wrx", "/tmp/app.#{$$}"
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 959
def chown(user, group, list, noop: nil, verbose: nil)
list = fu_list(list)
fu_output_message sprintf('chown %s %s',
(group ? "#{user}:#{group}" : user || ':'),
list.join(' ')) if verbose
return if noop
uid = fu_get_uid(user)
gid = fu_get_gid(group)
list.each do |path|
Entry_.new(path).chown uid, gid
end
end Изменяет владельца и группу указанных файлов (в list) на пользователя user и группу group. user и group могут быть идентификатором (целое число/строка) или именем (строка). Если user или group равно nil, этот метод не изменяет атрибут.
Bundler::FileUtils.chown 'root', 'staff', '/usr/local/bin/ruby'
Bundler::FileUtils.chown nil, 'bin', Dir.glob('/usr/bin/*'), :verbose => true
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 983
def chown_R(user, group, list, noop: nil, verbose: nil, force: nil)
list = fu_list(list)
fu_output_message sprintf('chown -R%s %s %s',
(force ? 'f' : ''),
(group ? "#{user}:#{group}" : user || ':'),
list.join(' ')) if verbose
return if noop
uid = fu_get_uid(user)
gid = fu_get_gid(group)
list.each do |root|
Entry_.new(root).traverse do |ent|
begin
ent.chown uid, gid
rescue
raise unless force
end
end
end
end Рекурсивно изменяет владельца и группу указанных файлов (в list) на пользователя user и группу group.
user и group могут быть идентификатором (целое число/строка) или именем (строка). Если user или group равно nil, этот метод не изменяет атрибут.
Bundler::FileUtils.chown_R 'www', 'www', '/var/www/htdocs' Bundler::FileUtils.chown_R 'cvs', 'cvs', '/var/cvs', :verbose => true
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 725
def compare_file(a, b)
return false unless File.size(a) == File.size(b)
File.open(a, 'rb') {|fa|
File.open(b, 'rb') {|fb|
return compare_stream(fa, fb)
}
}
end Возвращает true, если содержимое файла a и файла b идентичны.
Bundler::FileUtils.compare_file('somefile', 'somefile') #=> true
Bundler::FileUtils.compare_file('/dev/null', '/dev/urandom') #=> false
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 743
def compare_stream(a, b)
bsize = fu_stream_blksize(a, b)
sa = String.new(capacity: bsize)
sb = String.new(capacity: bsize)
begin
a.read(bsize, sa)
b.read(bsize, sb)
return true if sa.empty? && sb.empty?
end while sa == sb
false
end Возвращает true, если содержимое потока a и потока b идентичны.
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 412
def copy_entry(src, dest, preserve = false, dereference_root = false, remove_destination = false)
Entry_.new(src, nil, dereference_root).wrap_traverse(proc do |ent|
destent = Entry_.new(dest, ent.rel, false)
File.unlink destent.path if remove_destination && File.file?(destent.path)
ent.copy destent.path
end, proc do |ent|
destent = Entry_.new(dest, ent.rel, false)
ent.copy_metadata destent.path if preserve
end)
end Копирует элемент файловой системы src в dest.
Если src — это каталог, этот метод рекурсивно копирует его содержимое. Метод сохраняет типы файлов, например, символические ссылки, каталоги… (FIFO, файлы устройств и т. д. пока не поддерживаются)
И src, и dest должны быть именами путей. src должен существовать, dest не должен существовать.
Если preserve равно true, этот метод сохраняет владельца, группу и время последнего изменения. Разрешения копируются независимо от preserve.
Если dereference_root равно true, этот метод разыменовывает корень дерева.
Если remove_destination равно true, этот метод удаляет каждый целевой файл перед копированием.
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 428 def copy_file(src, dest, preserve = false, dereference = true) ent = Entry_.new(src, nil, dereference) ent.copy_file dest ent.copy_metadata dest if preserve end
Копирует содержимое файла src в dest.
И src, и dest должны быть именами путей.
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 440 def copy_stream(src, dest) IO.copy_stream(src, dest) end
Копирует поток src в dest.
src должен отвечать на read(n), а dest должен отвечать на write(str).
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 353
def cp(src, dest, preserve: nil, noop: nil, verbose: nil)
fu_output_message "cp#{preserve ? ' -p' : ''} #{[src,dest].flatten.join ' '}" if verbose
return if noop
fu_each_src_dest(src, dest) do |s, d|
copy_file s, d, preserve
end
end Копирует содержимое файла src в dest.
Если dest — это каталог, копирует src в dest/src.
Если src — это список файлов, то dest должен быть каталогом.
Bundler::FileUtils.cp 'eval.c', 'eval.c.org' Bundler::FileUtils.cp %w(cgi.rb complex.rb date.rb), '/usr/lib/ruby/1.6' Bundler::FileUtils.cp %w(cgi.rb complex.rb date.rb), '/usr/lib/ruby/1.6', :verbose => true Bundler::FileUtils.cp 'symlink', 'dest' # copy content, "dest" is not a symlink
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 386
def cp_r(src, dest, preserve: nil, noop: nil, verbose: nil,
dereference_root: true, remove_destination: nil)
fu_output_message "cp -r#{preserve ? 'p' : ''}#{remove_destination ? ' --remove-destination' : ''} #{[src,dest].flatten.join ' '}" if verbose
return if noop
fu_each_src_dest(src, dest) do |s, d|
copy_entry s, d, preserve, dereference_root, remove_destination
end
end Копирует src в dest.
Если src — это каталог, этот метод рекурсивно копирует все его содержимое. Если dest — это каталог, копирует src в dest/src.
src может быть списком файлов.
# Installing Ruby library "mylib" under the site_ruby
Bundler::FileUtils.rm_r site_ruby + '/mylib', :force
Bundler::FileUtils.cp_r 'lib/', site_ruby + '/mylib'
# Examples of copying several files to target directory.
Bundler::FileUtils.cp_r %w(mail.rb field.rb debug/), site_ruby + '/tmail'
Bundler::FileUtils.cp_r Dir.glob('*.rb'), '/home/foo/lib/ruby', :noop => true, :verbose => true
# If you want to copy all contents of a directory instead of the
# directory itself, c.f. src/x -> dest/x, src/y -> dest/y,
# use following code.
Bundler::FileUtils.cp_r 'src/.', 'dest' # cp_r('src', 'dest') makes dest/src,
# but this doesn't.
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 764
def install(src, dest, mode: nil, owner: nil, group: nil, preserve: nil,
noop: nil, verbose: nil)
if verbose
msg = +"install -c"
msg << ' -p' if preserve
msg << ' -m ' << mode_to_s(mode) if mode
msg << " -o #{owner}" if owner
msg << " -g #{group}" if group
msg << ' ' << [src,dest].flatten.join(' ')
fu_output_message msg
end
return if noop
uid = fu_get_uid(owner)
gid = fu_get_gid(group)
fu_each_src_dest(src, dest) do |s, d|
st = File.stat(s)
unless File.exist?(d) and compare_file(s, d)
remove_file d, true
copy_file s, d
File.utime st.atime, st.mtime, d if preserve
File.chmod fu_mode(mode, st), d if mode
File.chown uid, gid, d if uid or gid
end
end
end Если src не совпадает с dest, копирует его и изменяет режим разрешений на mode.
Если dest — это каталог, назначение — dest/src.
Метод удаляет назначение перед копированием.
Bundler::FileUtils.install 'ruby', '/usr/local/bin/ruby', :mode => 0755, :verbose => true Bundler::FileUtils.install 'lib.rb', '/usr/local/lib/ruby/site_ruby', :verbose => true
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 283
def ln(src, dest, force: nil, noop: nil, verbose: nil)
fu_output_message "ln#{force ? ' -f' : ''} #{[src,dest].flatten.join ' '}" if verbose
return if noop
fu_each_src_dest0(src, dest) do |s,d|
remove_file d, true if force
File.link s, d
end
end В первом случае создаётся жёсткая ссылка link, которая указывает на target. Если link уже существует, генерируется исключение Errno::EEXIST. Но если установлен параметр :force, link перезаписывается.
Bundler::FileUtils.ln 'gcc', 'cc', verbose: true Bundler::FileUtils.ln '/usr/bin/emacs21', '/usr/bin/emacs'
Во втором случае создаётся ссылка dir/target, указывающая на target. В третьем случае создаётся несколько жёстких ссылок в каталоге dir, указывающих на каждый элемент в targets. Если dir не является каталогом, генерируется исключение Errno::ENOTDIR.
Bundler::FileUtils.cd '/sbin' Bundler::FileUtils.ln %w(cp mv mkdir), '/bin' # Now /sbin/cp and /bin/cp are linked.
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 316
def ln_s(src, dest, force: nil, noop: nil, verbose: nil)
fu_output_message "ln -s#{force ? 'f' : ''} #{[src,dest].flatten.join ' '}" if verbose
return if noop
fu_each_src_dest0(src, dest) do |s,d|
remove_file d, true if force
File.symlink s, d
end
end В первом случае создаётся символическая ссылка link, которая указывает на target. Если link уже существует, генерируется исключение Errno::EEXIST. Но если установлен параметр :force, link перезаписывается.
Bundler::FileUtils.ln_s '/usr/bin/ruby', '/usr/local/bin/ruby' Bundler::FileUtils.ln_s 'verylongsourcefilename.c', 'c', force: true
Во втором случае создаётся ссылка dir/target, указывающая на target. В третьем случае создаётся несколько символических ссылок в каталоге dir, указывающих на каждый элемент в targets. Если dir не является каталогом, генерируется исключение Errno::ENOTDIR.
Bundler::FileUtils.ln_s Dir.glob('/bin/*.rb'), '/home/foo/bin'
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 337 def ln_sf(src, dest, noop: nil, verbose: nil) ln_s src, dest, force: true, noop: noop, verbose: verbose end
То же, что и
Bundler::FileUtils.ln_s(*args, force: true)
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 160
def mkdir(list, mode: nil, noop: nil, verbose: nil)
list = fu_list(list)
fu_output_message "mkdir #{mode ? ('-m %03o ' % mode) : ''}#{list.join ' '}" if verbose
return if noop
list.each do |dir|
fu_mkdir dir, mode
end
end Создаёт один или несколько каталогов.
Bundler::FileUtils.mkdir 'test' Bundler::FileUtils.mkdir %w( tmp data ) Bundler::FileUtils.mkdir 'notexist', :noop => true # Does not really create. Bundler::FileUtils.mkdir 'tmp', :mode => 0700
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 186
def mkdir_p(list, mode: nil, noop: nil, verbose: nil)
list = fu_list(list)
fu_output_message "mkdir -p #{mode ? ('-m %03o ' % mode) : ''}#{list.join ' '}" if verbose
return *list if noop
list.map {|path| remove_trailing_slash(path)}.each do |path|
# optimize for the most common case
begin
fu_mkdir path, mode
next
rescue SystemCallError
next if File.directory?(path)
end
stack = []
until path == stack.last # dirname("/")=="/", dirname("C:/")=="C:/"
stack.push path
path = File.dirname(path)
end
stack.pop # root directory should exist
stack.reverse_each do |dir|
begin
fu_mkdir dir, mode
rescue SystemCallError
raise unless File.directory?(dir)
end
end
end
return *list
end Создаёт каталог и все его родительские каталоги. Например,
Bundler::FileUtils.mkdir_p '/usr/local/lib/ruby'
приводит к созданию следующих каталогов, если они не существуют.
-
/usr
-
/usr/local
-
/usr/local/lib
-
/usr/local/lib/ruby
Вы можете передать несколько каталогов одновременно в списке.
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 455
def mv(src, dest, force: nil, noop: nil, verbose: nil, secure: nil)
fu_output_message "mv#{force ? ' -f' : ''} #{[src,dest].flatten.join ' '}" if verbose
return if noop
fu_each_src_dest(src, dest) do |s, d|
destent = Entry_.new(d, nil, true)
begin
if destent.exist?
if destent.directory?
raise Errno::EEXIST, d
else
destent.remove_file if rename_cannot_overwrite_file?
end
end
begin
File.rename s, d
rescue Errno::EXDEV
copy_entry s, d, true
if secure
remove_entry_secure s, force
else
remove_entry s, force
end
end
rescue SystemCallError
raise unless force
end
end
end Перемещает файл(ы) src в dest. Если file и dest находятся на разных разделах диска, файл копируется, а затем удаляется исходный файл.
Bundler::FileUtils.mv 'badname.rb', 'goodname.rb'
Bundler::FileUtils.mv 'stuff.rb', '/notexist/lib/ruby', :force => true # no error
Bundler::FileUtils.mv %w(junk.txt dust.txt), '/home/foo/.trash/'
Bundler::FileUtils.mv Dir.glob('test*.rb'), 'test', :noop => true, :verbose => true
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 98 def pwd Dir.pwd end
Возвращает имя текущего каталога.
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 714 def remove_dir(path, force = false) remove_entry path, force # FIXME?? check if it is a directory end
Удаляет каталог dir и его содержимое рекурсивно. Этот метод игнорирует StandardError, если force равно true.
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 686
def remove_entry(path, force = false)
Entry_.new(path).postorder_traverse do |ent|
begin
ent.remove
rescue
raise unless force
end
end
rescue
raise unless force
end Этот метод удаляет элемент файловой системы path. path может быть обычным файлом, каталогом или чем-то другим. Если path является каталогом, удалите его рекурсивно.
См. также remove_entry_secure.
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 609
def remove_entry_secure(path, force = false)
unless fu_have_symlink?
remove_entry path, force
return
end
fullpath = File.expand_path(path)
st = File.lstat(fullpath)
unless st.directory?
File.unlink fullpath
return
end
# is a directory.
parent_st = File.stat(File.dirname(fullpath))
unless parent_st.world_writable?
remove_entry path, force
return
end
unless parent_st.sticky?
raise ArgumentError, "parent directory is world writable, Bundler::FileUtils#remove_entry_secure does not work; abort: #{path.inspect} (parent directory mode #{'%o' % parent_st.mode})"
end
# freeze tree root
euid = Process.euid
File.open(fullpath + '/.') {|f|
unless fu_stat_identical_entry?(st, f.stat)
# symlink (TOC-to-TOU attack?)
File.unlink fullpath
return
end
f.chown euid, -1
f.chmod 0700
unless fu_stat_identical_entry?(st, File.lstat(fullpath))
# TOC-to-TOU attack?
File.unlink fullpath
return
end
}
# ---- tree root is frozen ----
root = Entry_.new(path)
root.preorder_traverse do |ent|
if ent.directory?
ent.chown euid, -1
ent.chmod 0700
end
end
root.postorder_traverse do |ent|
begin
ent.remove
rescue
raise unless force
end
end
rescue
raise unless force
end Этот метод удаляет элемент файловой системы path. path должен быть обычным файлом, каталогом или чем-то другим. Если path является каталогом, удалите его рекурсивно. Этот метод необходим для предотвращения уязвимости TOCTTOU (времени проверки-времени использования) локальной безопасности rm_r. rm_r создаёт брешь в безопасности, когда:
-
Родительский каталог имеет права записи для всех (включая /tmp).
-
Удаляемая древовидная структура каталогов включает каталог с правами записи для всех.
-
В системе есть символические ссылки.
Чтобы избежать этой бреши в безопасности, этот метод применяет специальную предобработку. Если path является каталогом, этот метод использует chown(2) и chmod(2) для всех удаляемых каталогов. Это требует, чтобы текущий процесс был владельцем удаляемой всей древовидной структуры каталогов или был суперпользователем (root).
ПРЕДУПРЕЖДЕНИЕ: Вы должны убедиться, что ВСЕ родительские каталоги не могут быть изменены другими недоверенными пользователями. Например, родительские каталоги не должны принадлежать недоверенным пользователям и не должны иметь права записи для всех, за исключением случаев, когда установлен битовый атрибут «вклейки».
ПРЕДУПРЕЖДЕНИЕ: Только владелец удаляемой древовидной структуры каталогов или суперпользователь Unix (root) должен вызывать этот метод. В противном случае этот метод не работает.
Для получения подробностей об этой уязвимости безопасности см. примеры в Perl:
Для fileutils.rb эта уязвимость была сообщена в [ruby-dev:26100].
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 703 def remove_file(path, force = false) Entry_.new(path).remove_file rescue raise unless force end
Удаляет файл path. Этот метод игнорирует StandardError, если force равно true.
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 501
def rm(list, force: nil, noop: nil, verbose: nil)
list = fu_list(list)
fu_output_message "rm#{force ? ' -f' : ''} #{list.join ' '}" if verbose
return if noop
list.each do |path|
remove_file path, force
end
end Удаляет файлы, указанные в list. Данный метод не может удалять каталоги. Все StandardErrors игнорируются, если установлен параметр :force.
Bundler::FileUtils.rm %w( junk.txt dust.txt )
Bundler::FileUtils.rm Dir.glob('*.so')
Bundler::FileUtils.rm 'NotExistFile', :force => true # never raises exception
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 520 def rm_f(list, noop: nil, verbose: nil) rm list, force: true, noop: noop, verbose: verbose end
Эквивалентно
Bundler::FileUtils.rm(list, :force => true)
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 547
def rm_r(list, force: nil, noop: nil, verbose: nil, secure: nil)
list = fu_list(list)
fu_output_message "rm -r#{force ? 'f' : ''} #{list.join ' '}" if verbose
return if noop
list.each do |path|
if secure
remove_entry_secure path, force
else
remove_entry path, force
end
end
end Удаляет файлы list[0] list[1]. Если list[n] является каталогом, удаляет все его содержимое рекурсивно. Этот метод игнорирует StandardError, если установлен параметр :force.
Bundler::FileUtils.rm_r Dir.glob('/tmp/*')
Bundler::FileUtils.rm_r 'some_dir', :force => true
ВНИМАНИЕ: Этот метод может привести к уязвимости, если один из родительских каталогов или удаляемая структура каталогов имеют права записи для всех (включая /tmp, права которого составляют 1777), а текущий процесс имеет сильные привилегии, такие как суперпользователь Unix (root), и система имеет символические ссылки. Для безопасного удаления внимательно ознакомьтесь с документацией remove_entry_secure и установите параметр :secure в значение true. По умолчанию :secure=>false.
ПРИМЕЧАНИЕ: Этот метод вызывает remove_entry_secure, если установлен параметр :secure. См. также remove_entry_secure.
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 569 def rm_rf(list, noop: nil, verbose: nil, secure: nil) rm_r list, force: true, noop: noop, verbose: verbose, secure: secure end
Эквивалентно
Bundler::FileUtils.rm_r(list, :force => true)
ВНИМАНИЕ: Этот метод может привести к локальной уязвимости. Сначала ознакомьтесь с документацией rm_r.
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 243
def rmdir(list, parents: nil, noop: nil, verbose: nil)
list = fu_list(list)
fu_output_message "rmdir #{parents ? '-p ' : ''}#{list.join ' '}" if verbose
return if noop
list.each do |dir|
begin
Dir.rmdir(dir = remove_trailing_slash(dir))
if parents
until (parent = File.dirname(dir)) == '.' or parent == dir
dir = parent
Dir.rmdir(dir)
end
end
rescue Errno::ENOTEMPTY, Errno::EEXIST, Errno::ENOENT
end
end
end Удаляет один или несколько каталогов.
Bundler::FileUtils.rmdir 'somedir' Bundler::FileUtils.rmdir %w(somedir anydir otherdir) # Does not really remove directory; outputs message. Bundler::FileUtils.rmdir 'somedir', :verbose => true, :noop => true
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 1042
def touch(list, noop: nil, verbose: nil, mtime: nil, nocreate: nil)
list = fu_list(list)
t = mtime
if verbose
fu_output_message "touch #{nocreate ? '-c ' : ''}#{t ? t.strftime('-t %Y%m%d%H%M.%S ') : ''}#{list.join ' '}"
end
return if noop
list.each do |path|
created = nocreate
begin
File.utime(t, t, path)
rescue Errno::ENOENT
raise if created
File.open(path, 'a') {
;
}
created = true
retry if t
end
end
end Обновляет время изменения (mtime) и время доступа (atime) файла(ов) в list. Файлы создаются, если они не существуют.
Bundler::FileUtils.touch 'timestamp'
Bundler::FileUtils.touch Dir.glob('*.c'); system 'make'
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 135
def uptodate?(new, old_list)
return false unless File.exist?(new)
new_time = File.mtime(new)
old_list.each do |old|
if File.exist?(old)
return false unless new_time > File.mtime(old)
end
end
true
end Возвращает true, если new новее всех old_list. Несуществующие файлы считаются более старыми, чем любой другой файл.
Bundler::FileUtils.uptodate?('hello.o', %w(hello.c hello.h)) or \
system 'make hello.o'
Ruby Core © 1993–2017 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.