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

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?

3 Likes

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.)

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?

1 Like

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.

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.

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.

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?

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.

3 Likes

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.

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.

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).

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.

1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.