当/ etc / {passwd,shadow,group}是符号链接(debian squeeze)时,不能使用useradd / adduser,

当我将/ etc / passwd / etc / shadow / etc / group从/ etc移动到/ home并创build符号链接以使/ etc / {passwd,shadow,group}分别指向/家用/ {passwd文件,阴影,组}

我不能创build任何用户,并有useradd输出:

root@client:/home# useradd testuser Adding user `testuser' ... Adding new group `testuser' (1000) ... groupadd: cannot open /etc/group 

btw useradd输出是

 root@client:/home# adduser testuser useradd: cannot open /etc/passwd 

为什么useradd拒绝打开一个符号链接的/etc/passwd

为了回答这个问题,我们需要看看useradd的源代码(我在Ubuntu 12.04上做了这个,在Debian上它可能会有所不同):

  1. 找出哪个包拥有/usr/sbin/useradd

     $ dpkg-query -S /usr/sbin/useradd passwd: /usr/sbin/useradd 
  2. 安装源代码:

     $ 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 (...) 
  3. cd到源目录:

     $ cd shadow-4.1.4.2+svn3283/ 
  4. searchuseradd的源文件的目录,理想的应该是useradd.c

     $ find . -name useradd.c ./src/useradd.c 

    答对了!

  5. 找错误信息cannot open /etc/passwd (其实我只searchcannot open ,因为整个string不返回任何结果):

     $ 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表示在匹配行之前打印1行前导上下文。

    这是您看到的错误信息正在生成的地方。 函数pw_open控制是否可以打开/etc/passwd或者抛出错误。

    pw_open不是Linux 系统调用 ( apropos pw_open不返回任何结果),所以它可能在这个包中实现。 我们来search一下吧

  6. 跟踪pw_open导致:

     $ grep -R pw_open * (...) lib/pwio.c:int pw_open (int mode) (...) 

    pw_open实现是:

     $ grep -A 3 'int pw_open (int mode)' lib/pwio.c int pw_open (int mode) { return commonio_open (&passwd_db, mode); } 

    越来越近,但我们还没有。 commonio_open是我们的新目标。

  7. searchcommonio_open

     $ grep -R commonio_open * (...) lib/commonio.c:int commonio_open (struct commonio_db *db, int mode) 
  8. 打开lib/commonio.c并滚动到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); 

    你看到O_NOFOLLOW吗? 这是罪魁祸首(从man 2 open ):

     O_NOFOLLOW If pathname is a symbolic link, then the open fails. 

总结一下, useradd.c使用了pw_open ,后者又使用commonio_open打开/etc/passwd使用open的选项O_NOFOLLOW open系统调用,拒绝符号链接。

尽pipe一个符号链接可以用来代替许多文件(我会说是最多的),但是useradd是相当挑剔的,可能是因为符号链接的/etc/passwd强烈暗示/etc已被篡改。

为什么我应该在/etc留下passwd

/etc有几个文件需要引导和login,例如(但不限于): fstabinittabpasswdshadowinit.d/的init脚本。 任何系统pipe理员都期望这些文件在那里,而不是符号链接到/home或任何地方。

所以即使你可以,你也应该在/etc留下passwd

此外,Linux中的文件系统结构已经定义好了,请看这里: http : //www.pathname.com/fhs/pub/fhs-2.3.html 。 还有一个/etc的章节。 移动的东西不build议。

医生,当我这样做的时候很痛

那么不要这样做!

严重的是,不要把文件粘贴在意想不到的地方。 无论你想要testing什么:find一个更好的方法。 如果您正在尝试执行中央身份validation,请使用ldap。 或者如果必须的话。