Why does useradd
refuse to open a symlinked /etc/passwd
?
To answer the question we need to take a look at the source code of useradd
(I did this on Ubuntu 12.04, on Debian it may differ slightly):
Find out which package owns /usr/sbin/useradd
:
$ dpkg-query -S /usr/sbin/useradd
passwd: /usr/sbin/useradd
Install the source:
$ apt-get source passwd
Reading package lists... Done
Building dependency tree
Reading state information... Done
Picking 'shadow' as source package instead of 'passwd'
(...)
dpkg-source: info: extracting shadow in shadow-4.1.4.2+svn3283
dpkg-source: info: unpacking shadow_4.1.4.2+svn3283.orig.tar.gz
dpkg-source: info: applying shadow_4.1.4.2+svn3283-3ubuntu5.1.diff.gz
(...)
cd
to the source directory:
$ cd shadow-4.1.4.2+svn3283/
Search the directory for useradd
's source file, which ideally should be called useradd.c
:
$ find . -name useradd.c
./src/useradd.c
Bingo!
Look for error message cannot open /etc/passwd
(in fact I only search for cannot open
, since the whole string doesn't return any results):
$ grep -B 1 'cannot open' src/useradd.c
(...)
if (pw_open (O_RDWR) == 0) {
fprintf (stderr, _("%s: cannot open %s\n"), Prog, pw_dbname ());
(...)
-B 1
means print 1 line of leading context before the matching line.
This is where the error message you see is being generated. Function pw_open
controls whether /etc/passwd
can be opened or an error should be thrown.
pw_open
is not a Linux syscall (apropos pw_open
doesn't return any results), so it is probably implemented within this package. Let's search for it.
Tracing pw_open
leads to:
$ grep -R pw_open *
(...)
lib/pwio.c:int pw_open (int mode)
(...)
pw_open
implementation is:
$ grep -A 3 'int pw_open (int mode)' lib/pwio.c
int pw_open (int mode)
{
return commonio_open (&passwd_db, mode);
}
Getting closer, but we're not there yet. commonio_open
is our new objective.
Search for commonio_open
:
$ grep -R commonio_open *
(...)
lib/commonio.c:int commonio_open (struct commonio_db *db, int mode)
Open lib/commonio.c
and scroll to function commonio_open
:
int commonio_open (struct commonio_db *db, int mode)
{
(...)
fd = open (db->filename,
(db->readonly ? O_RDONLY : O_RDWR)
| O_NOCTTY | O_NONBLOCK | O_NOFOLLOW);
Do you see O_NOFOLLOW
? This is the culprit (from man 2 open
):
O_NOFOLLOW
If pathname is a symbolic link, then the open fails.
Summarizing, useradd.c
uses pw_open
, which in turn uses commonio_open
, which opens /etc/passwd
using syscall open
with option O_NOFOLLOW
, that rejects symbolic links.
Although a symlink can be used as a replacement of a file in many (I'd say most) situations, useradd
is quite picky and rejects it, probably because a symlinked /etc/passwd
strongly suggests that /etc
has been tampered with.
Why should I leave passwd
in /etc
?
There are several files in /etc
needed to boot and log in, for example (but not limited to): fstab
, inittab
, passwd
, shadow
and the init scripts in init.d/
. Any sysadmin expects those files to be there, not symlinked to /home
or wherever.
So even if you could, you should leave passwd
in /etc
.
Furthermore, the filesystem structure in Linux is well defined, take a look at it here: http://www.pathname.com/fhs/pub/fhs-2.3.html. There is also a chapter for /etc
. Moving things around is not recommended.
/etc/passwd
and/etc/group
in particular are very long-standing UNIX concepts. Why are you trying to do this?/etc
and/home
reside in the same filesystem, you could use hardlinks (ln
without the-s
option).