Deprecate (or break-fix) std::env::home_dir


#1

This has nagged me for a long time, but the definition of home_dir in std is pretty arguably incorrect on Windows, and both rustup and cargo use their own custom definition.

The problem with it is that it prioritizes the HOME variable, which basically has no meaning on windows (the correct variable is “USERPROFILE”).

The practical result of this is that calls to home_dir on most windows environments result in a path like C:\Users\brson, but under cygwin and mingw result in things like /home/brian/.

Now, I have no doubt that is useful in some case, but I have not found that case yet, and find the function extremely misleading. When we found it in rustup and cargo, we felt like it was completely bogus and confusing behavior.

There might be a place for this function in the world, but I do not believe it is env::home_dir, and suggest deprecating it.

Thoughts?


#2

We have tried to fix this function before at https://github.com/rust-lang/rust/pull/46799 but basically arrived at the same conclusion (deprecate it eventually, point to an external package instead.)


#3

It looks like there’s a universal agreement that the current implementation is wrong, and the proposed one is better.

Wouldn’t that technically-breaking change be perceived merely as a bug fix?


#4

I commented on @alexcrichton’s main objection to break-fixing home_dir. Both cargo and multirust still use it for - at this point - ancient backcompat hacks, that I expect to be removed. In particularl, https://github.com/rust-lang/cargo/pull/5183 rewrites how cargo home is discovered and does not take into account the hack.

Edit: the functions that cargo/rustup use are not home_dir - they are cargo/rustup specific. home::home_dir is exactly like env::home_dir but without considering %HOME on windows.


#5

Anyway, just deprecating it is fine too. It’s just a bunk function and nobody should use it. And the home crate basically looks correct to me (for the home function specifically), besides the fact that on unix it calls std::env::home, and would need to be rewritten.

Once the cargo/rustup directory overhaul is done, the rest of the contents of the home crate will probably just be dead code, and home can literally contain one correct home_dir function.

On the other hand, we could just put a correct function in std - and I honestly doubt anybody is going to ding us for just fixing env::home_dir.


#6

We could also point peaple at https://github.com/soc/directories-rs, though I don’t know if that is using a similar definition of HOME - I know it doesn’t care about %USERPROFILE% on windows, but stackoverflow also says not to use `%USERPROFILE%. My only doubt about that crate is that the scope is big, and its opinionated.

The definition of home though is probably fine, though I believe incomplete on some platforms Rust supports.

Edit: directories is MPL licensed :-/

Edit: heh, on unix, directories just defers to std::env::home(), just like home.


#7

I do know that some people set %HOME% on Windows and expect that to work. And people who run programs under cygwin sometimes expect that behavior.

Perhaps we should just document the behavior very carefully, and also tell people what they should use if they don’t want that behavior?


#8

I mean, we could, but I’m pretty convinced it’s the wrong default. Having this weird home_dir function in std and no “correct” function is a pretty big wart.


#9

I agree with Brian. I think the best thing would be to just fix std::env::home and thoroughly document its behavior.

Can be addressed.

Anything wrong with that? Please let me know if there is any issue with it, and I’ll look into fixing it.


#10

Another nail in std::env::home()'s coffin:

It uses getpwuid_r if $HOME isn’t set. This means that everyone intentionally creating a user without a home directory (for security reasons) gets a made-up home directory handed from getpwuid_r. I’m pretty sure that’s not the desired behavior for anyone.

I wonder what’s the purpose of returning Option – it would make sense without the getpwuid_r fallback, but probably not with it in place.


#11

This means that everyone intentionally creating a user without a home directory (for security reasons) gets a made-up home directory handed from getpwuid_r.

How do you create a user without a home directory? It’s a required field in /etc/passwd. It doesn’t have to be readable by the given user or even exist on disk, but whatever’s in that field is by definition the user’s home directory, and I’d expect it to be returned by a “get my home directory” function.


#12

Sorry, my claim was incorrect. I assumed that the -r option had that effect in addition to setting the login shell to something that prevents logging in.

I just tried it, and it seems getpwuid_r does not indicate an error, but returns (correctly) an empty string.

Do you have further information on the home directory being a required field in /etc/passwd? E. g. what happens if it isn’t set?


#13

Do you have further information on the home directory being a required field in /etc/passwd?

passwd(5) says: "/etc/passwd contains one line for each user account, with seven fields delimited by colons", so it’s not physically possible for the home directory field to be missing—or if it is, the record won’t have enough fields to be valid.

When you mention “the -r option”, I assume you’re talking about the useradd(8) command, whose -r option means “create a system account” My useradd(8) says: “Note that useradd will not create a home directory for such a user, regardless of the default setting in /etc/login.defs (CREATE_HOME).” I take that as meaning it won’t create a home directory on disk; I don’t know what it gets set to in the database.

On my Debian system, most service accounts seem to have their home directories set to various places in /var that don’t exist; for example, the “uucp” user’s home directory is the non-existent /var/spool/uucp. Two system users, “nobody” and “apt” have their home directory literally set to /nonexistent, which is apparently the Debian convention.


#14

On Arch Linux, the home for users who don’t care is / (a root directory), so yeah, it seems mandatory in a way. Of course, the value may be useless, but it exists, so providing it seems fine (it’s out of scope anyway for a language to try to determine what home paths are valid and which aren’t).


#15

@Screwtapello I agree with this, and the way user land tools work – but what happens when the home dir is not set in /etc/passwd?

I checked, and it seems you receive an empty string. Rust considers this empty string to be a valid home directory.

Now the question is whether this should be the case, and if one assumed this makes sense (I don’t – just as I don’t believe an empty $HOME env should be accepted as a valid home directory¹), under which circumstances can home_dir actually return None?

@xfix The issue is not about what happens when users don’t care and user land tools add some default value to /etc/passwd – it’s what happens when no value is given in /etc/passwd.

¹ Compare std::env::home_dirs claim:

Returns the value of the ‘HOME’ environment variable if it is set and not equal to the empty string.

… with its actual behavior:

std::env::set_var("HOME", "");
println!("{:?}", std::env::var_os("HOME")); // Some("")
println!("{:?}", std::env::home_dir());     // Some("")

From my perspective so far, home_dir should either

  • return T if the implementation in which any value is considered valid is kept and the documentation needs to be fixed to reflect that, or
  • return Option<T> and stop considering empty strings a valid home directory in the implementation, regardless of whether they come from $HOME or getpwuid.

#16

After some discussion on #rust, we have come up with cases in which home_dir should return None:

  • $HOME empty, no entry for the given uid from getuid in /etc/passwd
  • $HOME empty, getpwuid->pw_dir is empty

std::env::home_dir returns Some on both.


#17

what happens when the home dir is not set in /etc/passwd?

“set to an empty string” is not the same as “not set”.

However, I can’t think of a good reason to set it that way, and I can think of some reasons not to (for one, any app running as that user that tried to write something to “the home directory” would probably write it to the current directory, whatever it is, which I can imagine would be a pain in the ass to debug). In the interests of encouraging programs to behave sanely, I heartily endorse your suggested rules for when home_dir() should return None.