diff options
author | Jeremy Evans <code@jeremyevans.net> | 2023-01-16 13:29:43 -0800 |
---|---|---|
committer | Jeremy Evans <code@jeremyevans.net> | 2023-03-24 11:18:57 -0700 |
commit | 466ca7ae205126c7cac83735db887d69e293f816 (patch) | |
tree | 2d2598ac52e1853f6afb8ddd0b1337616fd2647d /spec | |
parent | 5d6579bd9129cfbd62702fb42b249338807a34a2 (diff) |
Add Dir.fchdir
This is useful for passing directory file descriptors over UNIX
sockets or to child processes to avoid TOCTOU vulnerabilities.
The implementation follows the Dir.chdir code.
This will raise NotImplementedError on platforms not supporting
both fchdir and dirfd.
Implements [Feature #19347]
Notes
Notes:
Merged: https://github.com/ruby/ruby/pull/7135
Diffstat (limited to 'spec')
-rw-r--r-- | spec/ruby/core/dir/fchdir_spec.rb | 78 |
1 files changed, 78 insertions, 0 deletions
diff --git a/spec/ruby/core/dir/fchdir_spec.rb b/spec/ruby/core/dir/fchdir_spec.rb new file mode 100644 index 0000000000..dde459e98e --- /dev/null +++ b/spec/ruby/core/dir/fchdir_spec.rb @@ -0,0 +1,78 @@ +require_relative '../../spec_helper' +require_relative 'fixtures/common' + +ruby_version_is '3.3' do + has_fchdir = begin + dir = Dir.new('.') + Dir.fchdir(dir.fileno) + true + rescue NotImplementedError + false + rescue Exception + true + ensure + dir.close + end + + if has_fchdir + describe "Dir.fchdir" do + before :all do + DirSpecs.create_mock_dirs + end + + after :all do + DirSpecs.delete_mock_dirs + end + + before :each do + @dirs = [Dir.new('.')] + @original = @dirs.first.fileno + end + + after :each do + Dir.fchdir(@original) + @dirs.each(&:close) + end + + it "changes to the specified directory" do + dir = Dir.new(DirSpecs.mock_dir) + @dirs << dir + Dir.fchdir dir.fileno + Dir.pwd.should == DirSpecs.mock_dir + end + + it "returns 0 when successfully changing directory" do + Dir.fchdir(@original).should == 0 + end + + it "returns the value of the block when a block is given" do + Dir.fchdir(@original) { :block_value }.should == :block_value + end + + it "changes to the specified directory for the duration of the block" do + pwd = Dir.pwd + dir = Dir.new(DirSpecs.mock_dir) + @dirs << dir + Dir.fchdir(dir.fileno) { Dir.pwd }.should == DirSpecs.mock_dir + Dir.pwd.should == pwd + end + + it "raises a SystemCallError if the file descriptor given is not valid" do + -> { Dir.fchdir -1 }.should raise_error(SystemCallError) + -> { Dir.fchdir(-1) { } }.should raise_error(SystemCallError) + end + + it "raises a SystemCallError if the file descriptor given is not for a directory" do + -> { Dir.fchdir $stdout.fileno }.should raise_error(SystemCallError) + -> { Dir.fchdir($stdout.fileno) { } }.should raise_error(SystemCallError) + end + end + else + describe "Dir.fchdir" do + it "raises NotImplementedError" do + -> { Dir.fchdir 1 }.should raise_error(NotImplementedError) + -> { Dir.fchdir(1) { } }.should raise_error(NotImplementedError) + end + end + end +end |