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/passwdand/etc/groupin particular are very long-standing UNIX concepts. Why are you trying to do this? – MadHatter Mar 25 '13 at 09:02/etcand/homereside in the same filesystem, you could use hardlinks (lnwithout the-soption). – jaume Mar 25 '13 at 09:07