Skip to content

Commit 2ea29be

Browse files
authored
Merge pull request #451 from Shopify/rwstauner/permission-error
Remove custom permission error code and restore path in read error
2 parents 34dba5c + c094e57 commit 2ea29be

File tree

7 files changed

+80
-68
lines changed

7 files changed

+80
-68
lines changed

ext/bootsnap/bootsnap.c

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -682,10 +682,14 @@ bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler, VALUE args
682682
VALUE output_data; /* return data, e.g. ruby hash or loaded iseq */
683683

684684
VALUE exception; /* ruby exception object to raise instead of returning */
685+
VALUE exception_message; /* ruby exception string to use instead of errno_provenance */
685686

686687
/* Open the source file and generate a cache key for it */
687688
current_fd = open_current_file(path, &current_key, &errno_provenance);
688-
if (current_fd < 0) goto fail_errno;
689+
if (current_fd < 0) {
690+
exception_message = path_v;
691+
goto fail_errno;
692+
}
689693

690694
/* Open the cache key if it exists, and read its cache key in */
691695
cache_fd = open_cache_file(cache_path, &cached_key, &errno_provenance);
@@ -695,6 +699,7 @@ bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler, VALUE args
695699
rb_funcall(rb_mBootsnap, instrumentation_method, 2, cache_fd == CACHE_MISS ? sym_miss : sym_stale, path_v);
696700
}
697701
} else if (cache_fd < 0) {
702+
exception_message = rb_str_new_cstr(cache_path);
698703
goto fail_errno;
699704
} else {
700705
/* True if the cache existed and no invalidating changes have occurred since
@@ -717,20 +722,29 @@ bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler, VALUE args
717722
else if (res == CACHE_UNCOMPILABLE) {
718723
/* If fetch_cached_data returned `Uncompilable` we fallback to `input_to_output`
719724
This happens if we have say, an unsafe YAML cache, but try to load it in safe mode */
720-
if ((input_data = bs_read_contents(current_fd, current_key.size, &errno_provenance)) == Qfalse) goto fail_errno;
725+
if ((input_data = bs_read_contents(current_fd, current_key.size, &errno_provenance)) == Qfalse){
726+
exception_message = path_v;
727+
goto fail_errno;
728+
}
721729
bs_input_to_output(handler, args, input_data, &output_data, &exception_tag);
722730
if (exception_tag != 0) goto raise;
723731
goto succeed;
724732
} else if (res == CACHE_MISS || res == CACHE_STALE) valid_cache = 0;
725-
else if (res == ERROR_WITH_ERRNO) goto fail_errno;
733+
else if (res == ERROR_WITH_ERRNO){
734+
exception_message = rb_str_new_cstr(cache_path);
735+
goto fail_errno;
736+
}
726737
else if (!NIL_P(output_data)) goto succeed; /* fast-path, goal */
727738
}
728739
close(cache_fd);
729740
cache_fd = -1;
730741
/* Cache is stale, invalid, or missing. Regenerate and write it out. */
731742

732743
/* Read the contents of the source file into a buffer */
733-
if ((input_data = bs_read_contents(current_fd, current_key.size, &errno_provenance)) == Qfalse) goto fail_errno;
744+
if ((input_data = bs_read_contents(current_fd, current_key.size, &errno_provenance)) == Qfalse){
745+
exception_message = path_v;
746+
goto fail_errno;
747+
}
734748

735749
/* Try to compile the input_data using input_to_storage(input_data) */
736750
exception_tag = bs_input_to_storage(handler, args, input_data, path_v, &storage_data);
@@ -767,6 +781,7 @@ bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler, VALUE args
767781
* No point raising an error */
768782
if (errno != ENOENT) {
769783
errno_provenance = "bs_fetch:unlink";
784+
exception_message = rb_str_new_cstr(cache_path);
770785
goto fail_errno;
771786
}
772787
}
@@ -785,7 +800,7 @@ bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler, VALUE args
785800
return output_data;
786801
fail_errno:
787802
CLEANUP;
788-
exception = rb_syserr_new(errno, errno_provenance);
803+
exception = rb_syserr_new_str(errno, exception_message);
789804
rb_exc_raise(exception);
790805
__builtin_unreachable();
791806
raise:

lib/bootsnap/compile_cache.rb

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ def UNCOMPILABLE.inspect
88
end
99

1010
Error = Class.new(StandardError)
11-
PermissionError = Class.new(Error)
1211

1312
def self.setup(cache_dir:, iseq:, yaml:, json:, readonly: false)
1413
if iseq
@@ -43,15 +42,6 @@ def self.setup(cache_dir:, iseq:, yaml:, json:, readonly: false)
4342
end
4443
end
4544

46-
def self.permission_error(path)
47-
cpath = Bootsnap::CompileCache::ISeq.cache_dir
48-
raise(
49-
PermissionError,
50-
"bootsnap doesn't have permission to write cache entries in '#{cpath}' " \
51-
"(or, less likely, doesn't have permission to read '#{path}')",
52-
)
53-
end
54-
5545
def self.supported?
5646
# only enable on 'ruby' (MRI) and TruffleRuby for POSIX (darwin, linux, *bsd), Windows (RubyInstaller2)
5747
%w[ruby truffleruby].include?(RUBY_ENGINE) &&

lib/bootsnap/compile_cache/iseq.rb

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,6 @@ def load_iseq(path)
8787
return nil if defined?(Coverage) && Bootsnap::CompileCache::Native.coverage_running?
8888

8989
Bootsnap::CompileCache::ISeq.fetch(path.to_s)
90-
rescue Errno::EACCES
91-
Bootsnap::CompileCache.permission_error(path)
9290
rescue RuntimeError => error
9391
if error.message =~ /unmatched platform/
9492
puts("unmatched platform for file #{path}")

lib/bootsnap/compile_cache/json.rb

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -74,16 +74,12 @@ def load_file(path, *args)
7474
return super unless (kwargs.keys - ::Bootsnap::CompileCache::JSON.supported_options).empty?
7575
end
7676

77-
begin
78-
::Bootsnap::CompileCache::Native.fetch(
79-
Bootsnap::CompileCache::JSON.cache_dir,
80-
File.realpath(path),
81-
::Bootsnap::CompileCache::JSON,
82-
kwargs,
83-
)
84-
rescue Errno::EACCES
85-
::Bootsnap::CompileCache.permission_error(path)
86-
end
77+
::Bootsnap::CompileCache::Native.fetch(
78+
Bootsnap::CompileCache::JSON.cache_dir,
79+
File.realpath(path),
80+
::Bootsnap::CompileCache::JSON,
81+
kwargs,
82+
)
8783
end
8884

8985
ruby2_keywords :load_file if respond_to?(:ruby2_keywords, true)

lib/bootsnap/compile_cache/yaml.rb

Lines changed: 24 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -229,16 +229,12 @@ def load_file(path, *args)
229229
return super unless (kwargs.keys - CompileCache::YAML.supported_options).empty?
230230
end
231231

232-
begin
233-
CompileCache::Native.fetch(
234-
CompileCache::YAML.cache_dir,
235-
File.realpath(path),
236-
CompileCache::YAML::Psych4::SafeLoad,
237-
kwargs,
238-
)
239-
rescue Errno::EACCES
240-
CompileCache.permission_error(path)
241-
end
232+
CompileCache::Native.fetch(
233+
CompileCache::YAML.cache_dir,
234+
File.realpath(path),
235+
CompileCache::YAML::Psych4::SafeLoad,
236+
kwargs,
237+
)
242238
end
243239

244240
ruby2_keywords :load_file if respond_to?(:ruby2_keywords, true)
@@ -253,16 +249,12 @@ def unsafe_load_file(path, *args)
253249
return super unless (kwargs.keys - CompileCache::YAML.supported_options).empty?
254250
end
255251

256-
begin
257-
CompileCache::Native.fetch(
258-
CompileCache::YAML.cache_dir,
259-
File.realpath(path),
260-
CompileCache::YAML::Psych4::UnsafeLoad,
261-
kwargs,
262-
)
263-
rescue Errno::EACCES
264-
CompileCache.permission_error(path)
265-
end
252+
CompileCache::Native.fetch(
253+
CompileCache::YAML.cache_dir,
254+
File.realpath(path),
255+
CompileCache::YAML::Psych4::UnsafeLoad,
256+
kwargs,
257+
)
266258
end
267259

268260
ruby2_keywords :unsafe_load_file if respond_to?(:ruby2_keywords, true)
@@ -309,16 +301,12 @@ def load_file(path, *args)
309301
return super unless (kwargs.keys - CompileCache::YAML.supported_options).empty?
310302
end
311303

312-
begin
313-
CompileCache::Native.fetch(
314-
CompileCache::YAML.cache_dir,
315-
File.realpath(path),
316-
CompileCache::YAML::Psych3,
317-
kwargs,
318-
)
319-
rescue Errno::EACCES
320-
CompileCache.permission_error(path)
321-
end
304+
CompileCache::Native.fetch(
305+
CompileCache::YAML.cache_dir,
306+
File.realpath(path),
307+
CompileCache::YAML::Psych3,
308+
kwargs,
309+
)
322310
end
323311

324312
ruby2_keywords :load_file if respond_to?(:ruby2_keywords, true)
@@ -333,16 +321,12 @@ def unsafe_load_file(path, *args)
333321
return super unless (kwargs.keys - CompileCache::YAML.supported_options).empty?
334322
end
335323

336-
begin
337-
CompileCache::Native.fetch(
338-
CompileCache::YAML.cache_dir,
339-
File.realpath(path),
340-
CompileCache::YAML::Psych3,
341-
kwargs,
342-
)
343-
rescue Errno::EACCES
344-
CompileCache.permission_error(path)
345-
end
324+
CompileCache::Native.fetch(
325+
CompileCache::YAML.cache_dir,
326+
File.realpath(path),
327+
CompileCache::YAML::Psych3,
328+
kwargs,
329+
)
346330
end
347331

348332
ruby2_keywords :unsafe_load_file if respond_to?(:ruby2_keywords, true)

test/compile_cache/yaml_test.rb

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,21 @@ def test_unsafe_load_file_supports_regexp
245245
end
246246
end
247247

248+
def test_no_read_permission
249+
if RbConfig::CONFIG["host_os"] =~ /mswin|mingw|cygwin/
250+
# On windows removing read permission doesn't prevent reading.
251+
pass
252+
else
253+
file = "a.yml"
254+
Help.set_file(file, ::YAML.dump(Object.new), 100)
255+
FileUtils.chmod(0o000, file)
256+
exception = assert_raises(Errno::EACCES) do
257+
FakeYaml.load_file(file)
258+
end
259+
assert_match(file, exception.message)
260+
end
261+
end
262+
248263
private
249264

250265
def with_default_encoding_internal(encoding)

test/compile_cache_test.rb

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ def test_no_write_permission_to_cache
3838
# list contents.
3939
#
4040
# Since we can't read directories on windows, this specific test doesn't
41-
# make sense. In addtion we test read-only files in
41+
# make sense. In addition we test read-only files in
4242
# `test_can_open_read_only_cache` so we are covered testing reading
4343
# read-only files.
4444
pass
@@ -51,6 +51,20 @@ def test_no_write_permission_to_cache
5151
end
5252
end
5353

54+
def test_no_read_permission
55+
if RbConfig::CONFIG["host_os"] =~ /mswin|mingw|cygwin/
56+
# On windows removing read permission doesn't prevent reading.
57+
pass
58+
else
59+
path = Help.set_file("a.rb", "a = a = 3", 100)
60+
FileUtils.chmod(0o000, path)
61+
exception = assert_raises(LoadError) do
62+
load(path)
63+
end
64+
assert_match(path, exception.message)
65+
end
66+
end
67+
5468
def test_can_open_read_only_cache
5569
path = Help.set_file("a.rb", "a = a = 3", 100)
5670
# Load once to create the cache file

0 commit comments

Comments
 (0)