Skip to content

Commit

Permalink
Resolve the current user home even when $HOME is not set
Browse files Browse the repository at this point in the history
* Fixes oracle#2784
* Use getpwuid_r(getuid()) for simplicity and reliability.
  • Loading branch information
eregon authored and sophie-kaleba committed Jan 17, 2023
1 parent 6d44734 commit 20c7cf9
Show file tree
Hide file tree
Showing 6 changed files with 85 additions and 6 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ Compatibility:
* Support `offset` keyword argument for `String#unpack` and `String#unpack1` (@andrykonchin).
* Fix `Process.detach` and cast `pid` argument to `Integer` (#2782, @andrykonchin).
* `rb_to_id()` should create a static `ID`, used by RMagick (@eregon).
* Resolve the current user home even when `$HOME` is not set (#2784, @eregon)

Performance:

Expand Down
2 changes: 1 addition & 1 deletion mx.truffleruby/native
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
GRAALVM_SKIP_ARCHIVE=true
DYNAMIC_IMPORTS=/tools,/compiler,/substratevm
COMPONENTS=TruffleRuby,suite:tools,GraalVM compiler,SubstrateVM
COMPONENTS=TruffleRuby,suite:tools,GraalVM compiler,SubstrateVM,Native Image
NATIVE_IMAGES=suite:sulong,lib:rubyvm
# To also create the standalone
DISABLE_INSTALLABLES=false
16 changes: 16 additions & 0 deletions spec/ruby/core/dir/home_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,18 @@
it "returns a non-frozen string" do
Dir.home.should_not.frozen?
end

it "returns a string with the filesystem encoding" do
Dir.home.encoding.should == Encoding.find("filesystem")
end

platform_is_not :windows do
it "works even if HOME is unset" do
ENV.delete('HOME')
Dir.home.should.start_with?('/')
Dir.home.encoding.should == Encoding.find("filesystem")
end
end
end

describe "when called with the current user name" do
Expand All @@ -37,6 +49,10 @@
it "returns a non-frozen string" do
Dir.home(ENV['USER']).should_not.frozen?
end

it "returns a string with the filesystem encoding" do
Dir.home(ENV['USER']).encoding.should == Encoding.find("filesystem")
end
end

it "raises an ArgumentError if the named user doesn't exist" do
Expand Down
46 changes: 46 additions & 0 deletions src/main/c/truffleposix/truffleposix.c
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,52 @@ int truffleposix_getrusage(double times[4]) {
return 0;
}

// Keep in sync with truffleposix_get_user_home() below
char* truffleposix_get_current_user_home(void) {
uid_t uid = getuid();

struct passwd entry;
struct passwd *result = NULL;
int ret;

size_t buffer_size = sysconf(_SC_GETPW_R_SIZE_MAX);
if (buffer_size <= 0) {
buffer_size = 16384;
}

char *buffer = malloc(buffer_size);
if (buffer == NULL) {
return NULL;
}

retry:
ret = getpwuid_r(uid, &entry, buffer, buffer_size, &result);
if (result != NULL) {
char *home = strdup(entry.pw_dir);
free(buffer);
return home;
} else if (ret == ERANGE) {
buffer_size *= 2;
free(buffer);
buffer = malloc(buffer_size);
if (buffer == NULL) {
return NULL;
}
goto retry;
} else if (ret == EINTR) {
goto retry;
} else if (ret == EIO || ret == EMFILE || ret == ENFILE) {
free(buffer);
errno = ret;
return NULL;
} else { // result == NULL, which means not found
// ret should be 0 in that case according to the man page, but it doesn't seem to always hold
free(buffer);
return strdup("");
}
}

// Keep in sync with truffleposix_get_current_user_home() above
char* truffleposix_get_user_home(const char *name) {
struct passwd entry;
struct passwd *result = NULL;
Expand Down
25 changes: 20 additions & 5 deletions src/main/ruby/truffleruby/core/dir.rb
Original file line number Diff line number Diff line change
Expand Up @@ -209,21 +209,36 @@ def exist?(path)
alias_method :exists?, :exist?

def home(user = nil)
if user
user = StringValue(user) unless Primitive.nil?(user)

if user and !user.empty?
ptr = Truffle::POSIX.truffleposix_get_user_home(user)
if !ptr.null?
home = ptr.read_string
home = ptr.read_string.force_encoding(Encoding.filesystem)
Truffle::POSIX.truffleposix_free ptr
raise ArgumentError, "user #{user} does not exist" if home.empty?
home
else
Errno.handle
end
else
home = ENV['HOME']
raise ArgumentError, "couldn't find HOME environment variable when expanding '~'" if Primitive.nil? home
home = ENV['HOME'].dup
if home
home = home.dup.force_encoding(Encoding.filesystem)
else
ptr = Truffle::POSIX.truffleposix_get_current_user_home
if !ptr.null?
home = ptr.read_string.force_encoding(Encoding.filesystem)
Truffle::POSIX.truffleposix_free ptr
raise ArgumentError, "couldn't find home for uid `#{Process.uid}'" if home.empty?
home
else
Errno.handle
end
end

raise ArgumentError, 'non-absolute home' unless home.start_with?('/')
home.dup
home
end
end

Expand Down
1 change: 1 addition & 0 deletions src/main/ruby/truffleruby/core/posix.rb
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,7 @@ def self.unsetenv(name)

# Other routines
attach_function :crypt, [:string, :string], :string, LIBCRYPT
attach_function :truffleposix_get_current_user_home, [], :pointer, LIBTRUFFLEPOSIX
attach_function :truffleposix_get_user_home, [:string], :pointer, LIBTRUFFLEPOSIX
attach_function :truffleposix_free, [:pointer], :void, LIBTRUFFLEPOSIX

Expand Down

0 comments on commit 20c7cf9

Please sign in to comment.