summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
authorJeremy Evans <code@jeremyevans.net>2023-01-16 13:29:43 -0800
committerJeremy Evans <code@jeremyevans.net>2023-03-24 11:18:57 -0700
commit466ca7ae205126c7cac83735db887d69e293f816 (patch)
tree2d2598ac52e1853f6afb8ddd0b1337616fd2647d /spec
parent5d6579bd9129cfbd62702fb42b249338807a34a2 (diff)
downloadruby-466ca7ae205126c7cac83735db887d69e293f816.tar.gz
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]
Diffstat (limited to 'spec')
-rw-r--r--spec/ruby/core/dir/fchdir_spec.rb78
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