diff mbox series

nss: look for databases in /usr/share/ if they don't exist in /etc/

Message ID ZvZj2jkpJjhaP-Yc@gardel-login
State New
Headers show
Series nss: look for databases in /usr/share/ if they don't exist in /etc/ | expand

Commit Message

Lennart Poettering Sept. 27, 2024, 7:50 a.m. UTC
In order to improve compatibility with systems that implement a
hermetic /usr/, this changes glibc NSS code to always look for its
databases files in /usr/share/ too in case they don't exist (ENOENT) in
/etc/. This allows distributions to move /etc/protocols and similar
data files into /usr/share/. These days various of these files are kinda
static anyway, and hence in many cases are better placed below the /usr/
hierarchy than below the configurable /etc/.

This change should have zero effect on existing systems, as any database
file in /etc/ will be consulted first, and only on ENOENT the matching
counterpart in /usr/share/ will be attempted. This also provides a clear
path how administrators can still modify the files locally if they want
to: just copy them from /usr/share/ to /etc/ and edit them there. In
that case the files in /usr/share/ will have no effect anymore.

Many systems that want to achieve this behaviour currently work around
glibc's behaviour via modules such as nss-altfiles, an excercise that
becomes unnecessary if glibc just directly looks at these fallback
places.

Also see:

https://github.com/uapi-group/specifications/issues/76
---
 include/nss_files.h                |  6 +++---
 nss/nss_compat/compat-grp.c        |  2 +-
 nss/nss_compat/compat-initgroups.c |  2 +-
 nss/nss_compat/compat-pwd.c        |  2 +-
 nss/nss_compat/compat-spwd.c       |  2 +-
 nss/nss_files/files-XXX.c          |  9 +++++----
 nss/nss_files/files-alias.c        |  9 +++++----
 nss/nss_files/files-initgroups.c   |  2 +-
 nss/nss_files/files-netgrp.c       |  5 +++--
 nss/nss_files_data.c               | 11 ++++++-----
 nss/nss_files_fopen.c              | 11 +++++++++--
 11 files changed, 36 insertions(+), 25 deletions(-)

--
2.46.0

Comments

Florian Weimer Sept. 27, 2024, 8:54 a.m. UTC | #1
* Lennart Poettering:

> In order to improve compatibility with systems that implement a
> hermetic /usr/, this changes glibc NSS code to always look for its
> databases files in /usr/share/ too in case they don't exist (ENOENT) in
> /etc/. This allows distributions to move /etc/protocols and similar
> data files into /usr/share/. These days various of these files are kinda
> static anyway, and hence in many cases are better placed below the /usr/
> hierarchy than below the configurable /etc/.

We don't really know what's in /usr/share.  There's some evidence that
/usr/share/services/ was used as a directory at one point.  The patch
isn't directly incompatible with that, it's just that it can't be used
to move /etc/services to /usr/share/services on such systems.

Can we use a subdirectory of /usr/share instead, or perhaps /usr/etc?

Thanks,
Florian
Lennart Poettering Sept. 27, 2024, 9:12 a.m. UTC | #2
On Fr, 27.09.24 10:54, Florian Weimer (fweimer@redhat.com) wrote:

> * Lennart Poettering:
>
> > In order to improve compatibility with systems that implement a
> > hermetic /usr/, this changes glibc NSS code to always look for its
> > databases files in /usr/share/ too in case they don't exist (ENOENT) in
> > /etc/. This allows distributions to move /etc/protocols and similar
> > data files into /usr/share/. These days various of these files are kinda
> > static anyway, and hence in many cases are better placed below the /usr/
> > hierarchy than below the configurable /etc/.
>
> We don't really know what's in /usr/share.  There's some evidence that
> /usr/share/services/ was used as a directory at one point.  The patch
> isn't directly incompatible with that, it's just that it can't be used
> to move /etc/services to /usr/share/services on such systems.
>
> Can we use a subdirectory of /usr/share instead, or perhaps /usr/etc?

Hmm, maybe it makes sense to treat NSS databases as something slightly
distinct from other config files in /etc/, hence, maybe let's use
/usr/share/nss/ for this, to indicate this is about nss databases?
After all these files are similarly structured, have similar
(sometimes identical) parsers, but have no file suffix that would tell
us that. By placing them in a new subdir of /usr/share/ called "nss"
we could communicate this similarity in structure a bit?

(Not too keen on /usr/etc/ because that introduces yet another
high-level hierarchy in /usr/. Maybe /usr/share/etc/ might be a middle
ground?)

Ultimately I don't care too much. As long as the files can be moved
somewhere below /usr/ I am fine with either. My personal preference
though would be in this order (if you already rule out /usr/share/
itself):

1. /usr/share/nss/ (clear preference from my side!)
2. /usr/share/etc/
3. /usr/etc/

Just let me know what to use, I'll rearrange the patch accordingly.

Lennart

--
Lennart Poettering, Berlin
Florian Weimer Sept. 27, 2024, 9:26 a.m. UTC | #3
* Lennart Poettering:

> On Fr, 27.09.24 10:54, Florian Weimer (fweimer@redhat.com) wrote:
>
>> * Lennart Poettering:
>>
>> > In order to improve compatibility with systems that implement a
>> > hermetic /usr/, this changes glibc NSS code to always look for its
>> > databases files in /usr/share/ too in case they don't exist (ENOENT) in
>> > /etc/. This allows distributions to move /etc/protocols and similar
>> > data files into /usr/share/. These days various of these files are kinda
>> > static anyway, and hence in many cases are better placed below the /usr/
>> > hierarchy than below the configurable /etc/.
>>
>> We don't really know what's in /usr/share.  There's some evidence that
>> /usr/share/services/ was used as a directory at one point.  The patch
>> isn't directly incompatible with that, it's just that it can't be used
>> to move /etc/services to /usr/share/services on such systems.
>>
>> Can we use a subdirectory of /usr/share instead, or perhaps /usr/etc?
>
> Hmm, maybe it makes sense to treat NSS databases as something slightly
> distinct from other config files in /etc/, hence, maybe let's use
> /usr/share/nss/ for this, to indicate this is about nss databases?
> After all these files are similarly structured, have similar
> (sometimes identical) parsers, but have no file suffix that would tell
> us that. By placing them in a new subdir of /usr/share/ called "nss"
> we could communicate this similarity in structure a bit?

The Netscape Security Services do not use /usr/share/nss, so that should
work.

> (Not too keen on /usr/etc/ because that introduces yet another
> high-level hierarchy in /usr/. Maybe /usr/share/etc/ might be a middle
> ground?)

I think openSUSE has been using /usr/etc for many years.  See “System
Databases (rpc, services, protocols)” in
<https://kubic.opensuse.org/blog/2019-12-05-usr-etc/>, and
<https://github.com/openSUSE/libnss_usrfiles>, particularly list of
pathnames there.  Cc:ing Thorsten.

I don't particularly like /usr/etc because it is inconsistent with
UsrMove (from a certain perspective), but this dislike doesn't go to the
level that we need to be incompatible with existing deployments.

Thanks,
Florian
Thorsten Kukuk Sept. 27, 2024, 10:26 a.m. UTC | #4
On Fri, Sep 27, 2024 at 11:26 AM Florian Weimer <fweimer@redhat.com> wrote:
>
> * Lennart Poettering:
>

> > Hmm, maybe it makes sense to treat NSS databases as something slightly
> > distinct from other config files in /etc/, hence, maybe let's use
> > /usr/share/nss/ for this, to indicate this is about nss databases?
> > After all these files are similarly structured, have similar
> > (sometimes identical) parsers, but have no file suffix that would tell
> > us that. By placing them in a new subdir of /usr/share/ called "nss"
> > we could communicate this similarity in structure a bit?
>
> The Netscape Security Services do not use /usr/share/nss, so that should
> work.
>
> > (Not too keen on /usr/etc/ because that introduces yet another
> > high-level hierarchy in /usr/. Maybe /usr/share/etc/ might be a middle
> > ground?)
>
> I think openSUSE has been using /usr/etc for many years.  See “System
> Databases (rpc, services, protocols)” in
> <https://kubic.opensuse.org/blog/2019-12-05-usr-etc/>, and
> <https://github.com/openSUSE/libnss_usrfiles>, particularly list of
> pathnames there.  Cc:ing Thorsten.
>
> I don't particularly like /usr/etc because it is inconsistent with
> UsrMove (from a certain perspective), but this dislike doesn't go to the
> level that we need to be incompatible with existing deployments.

We (openSUSE/SUSE) use /usr/etc only for files where there is no
upstream location.
If there is one, we will move the files to that location.
So if we agree on /usr/share/nss or /usr/share/etc (I personally have
no preference here), we would move the files from /usr/etc to this
/usr/share/... directory. Since no application accesses this files
directly, it's no problem for us.

Thorsten
Florian Weimer Sept. 27, 2024, 11:21 a.m. UTC | #5
* Thorsten Kukuk:

> We (openSUSE/SUSE) use /usr/etc only for files where there is no
> upstream location.
> If there is one, we will move the files to that location.

Thank you for offering that.

> So if we agree on /usr/share/nss or /usr/share/etc (I personally have
> no preference here), we would move the files from /usr/etc to this
> /usr/share/... directory. Since no application accesses this files
> directly, it's no problem for us.

The Go runtime needs to be taught about the alternative locations.
There are parallel consumers of the NSS configuration, such as libsubid
and sudo.  Likely there are more.  And of course we have a couple of
alternate libcs now.  It's not *that* much, but it's not just glibc
either.

Thanks,
Florian
Florian Weimer Sept. 30, 2024, 8 a.m. UTC | #6
* Florian Weimer:

> * Thorsten Kukuk:
>
>> We (openSUSE/SUSE) use /usr/etc only for files where there is no
>> upstream location.
>> If there is one, we will move the files to that location.
>
> Thank you for offering that.

Feedback from Colin Walters on the Fedora development list:

| Please avoid having projects unconditionally read `/usr/etc`
| because on ostree based systems `/usr/etc/services` will always
| exist; we use it for our 3-way merge of /etc.
| (It's actually also really nice to have the defaults always
|  visible, that's how `ostree admin config-diff` works)
| 
| It will start to conflict if other projects put things there.
| 
| Now our technical direction may go in a place where we
| move away from `/usr/etc` and do things differently
| (and this is a whole *other* big thing to align in api
|  probably).

<https://lists.fedoraproject.org/archives/list/devel@lists.fedoraproject.org/message/LQQ3X3QI4USLV6B6SSNURLU3GVY5EKVE/>

So I assume it should be /usr/share/nss then.

Thanks,
Florian
Florian Weimer Sept. 30, 2024, 8:02 a.m. UTC | #7
* Lennart Poettering:

>  /* Open PATH for reading, as a data source for nss_files.  */
> -FILE *__nss_files_fopen (const char *path);
> +FILE *__nss_files_fopen (const char *path, const char *fallback_path);
>  libc_hidden_proto (__nss_files_fopen)

I suggest to create a new function that takes just the database name as
a string (say "group") and constructs the file name from that.

We already have some :include: processing (and we probably want more),
so you probably should not change __nss_file_fopen itself.

Thanks,
Florian
Lennart Poettering Oct. 1, 2024, 9:04 a.m. UTC | #8
On Mo, 30.09.24 17:17, DJ Delorie (dj@redhat.com) wrote:

> Lennart Poettering <lennart@poettering.net> writes:
> > In order to improve compatibility with systems that implement a
> > hermetic /usr/, this changes glibc NSS code to always look for its
> > databases files in /usr/share/ too in case they don't exist (ENOENT) in
> > /etc/. This allows distributions to move /etc/protocols and similar
> > data files into /usr/share/. These days various of these files are kinda
> > static anyway, and hence in many cases are better placed below the /usr/
> > hierarchy than below the configurable /etc/.
>
> I'm opposed to the glibc sources knowing more about the OS choices than
> is needed.

The thing is that this really shouldn't be an OS choice. The files
themselves are API to some point, are being read (and manipulated) by
numerous tools. I think it's really important we push people towards
common paths for the fallback versions, to make this workable.

> We have existing ways of handling this:
>
> 1. Symbolic links (or other aggregators)

This contradicts the concept of hermetic /usr/, i.e. that you really
only need /usr/ to boot up, which is the point of the whole
excercise. if you require symlinks to be placed in /etc/, then well,
you didn't solve the problem that everyone currently tries to solve
independently, and differently. in the discussions it cam up that at
least we at microsoft, opensuse, fedora coreos, solus us came up with
different approaches to the same problem because they didn't want to
touch glibc so far.

> 2. ./configure options

Most installations don't want to simply move the files (i.e. have one
place for them), but want a "fallback" concept, i.e. (i.e. have two
places for them, one checked after the other). A configure option
alone wouldn't do.

> I note that glibc is lacking in #2 for NSS files, but I would not be
> opposed to adding support for such.  Perhaps, by definition, those
> options could be a list of files to search.
>
> $ ./configure --services-path=/etc/services:/usr/share/systemd/whatever/services
>
> Solving this problem by hard-coding *yet another* path choice is a step
> backwards.

I think this would be overkill: I am not aware of any of the existing
approaches needing a whole series of paths to check, they are all fine
with just one in /usr/.

Lennart

--
Lennart Poettering, Berlin
diff mbox series

Patch

diff --git a/include/nss_files.h b/include/nss_files.h
index adf934f3ea..852c64da99 100644
--- a/include/nss_files.h
+++ b/include/nss_files.h
@@ -26,7 +26,7 @@ 
 #endif

 /* Open PATH for reading, as a data source for nss_files.  */
-FILE *__nss_files_fopen (const char *path);
+FILE *__nss_files_fopen (const char *path, const char *fallback_path);
 libc_hidden_proto (__nss_files_fopen)

 /* Read a line from FP, storing it BUF.  Strip leading blanks and skip
@@ -89,7 +89,7 @@  enum nss_files_file
    null.  */
 enum nss_status __nss_files_data_open (struct nss_files_per_file_data **pdata,
                                        enum nss_files_file file,
-                                       const char *path,
+                                       const char *path, const char *fallback_path,
                                        int *errnop, int *herrnop);
 libc_hidden_proto (__nss_files_data_open)

@@ -101,7 +101,7 @@  libc_hidden_proto (__nss_files_data_put)
 /* Performs the set*ent operation for FILE.  PATH is the file to
    open.  */
 enum nss_status __nss_files_data_setent (enum nss_files_file file,
-                                           const char *path);
+                                         const char *path, const char *fallback_path);
 libc_hidden_proto (__nss_files_data_setent)

 /* Performs the end*ent operation for FILE.  */
diff --git a/nss/nss_compat/compat-grp.c b/nss/nss_compat/compat-grp.c
index ede7503de7..bacad5b583 100644
--- a/nss/nss_compat/compat-grp.c
+++ b/nss/nss_compat/compat-grp.c
@@ -108,7 +108,7 @@  internal_setgrent (ent_t *ent, int stayopen, int needent)

   if (ent->stream == NULL)
     {
-      ent->stream = __nss_files_fopen ("/etc/group");
+      ent->stream = __nss_files_fopen ("/etc/group", "/usr/share/group");

       if (ent->stream == NULL)
 	status = errno == EAGAIN ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL;
diff --git a/nss/nss_compat/compat-initgroups.c b/nss/nss_compat/compat-initgroups.c
index 2598cfbc9a..37eb4b17af 100644
--- a/nss/nss_compat/compat-initgroups.c
+++ b/nss/nss_compat/compat-initgroups.c
@@ -122,7 +122,7 @@  internal_setgrent (ent_t *ent)
   else
     ent->blacklist.current = 0;

-  ent->stream = __nss_files_fopen ("/etc/group");
+  ent->stream = __nss_files_fopen ("/etc/group", "/usr/share/group");

   if (ent->stream == NULL)
     status = errno == EAGAIN ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL;
diff --git a/nss/nss_compat/compat-pwd.c b/nss/nss_compat/compat-pwd.c
index 2f37e0f621..2a3b012c33 100644
--- a/nss/nss_compat/compat-pwd.c
+++ b/nss/nss_compat/compat-pwd.c
@@ -223,7 +223,7 @@  internal_setpwent (ent_t *ent, int stayopen, int needent)

   if (ent->stream == NULL)
     {
-      ent->stream = __nss_files_fopen ("/etc/passwd");
+      ent->stream = __nss_files_fopen ("/etc/passwd", "/usr/share/passwd");

       if (ent->stream == NULL)
 	status = errno == EAGAIN ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL;
diff --git a/nss/nss_compat/compat-spwd.c b/nss/nss_compat/compat-spwd.c
index fac1e766cb..fc6984bcb4 100644
--- a/nss/nss_compat/compat-spwd.c
+++ b/nss/nss_compat/compat-spwd.c
@@ -178,7 +178,7 @@  internal_setspent (ent_t *ent, int stayopen, int needent)

   if (ent->stream == NULL)
     {
-      ent->stream = __nss_files_fopen ("/etc/shadow");
+      ent->stream = __nss_files_fopen ("/etc/shadow", "/usr/share/shadow");

       if (ent->stream == NULL)
 	status = errno == EAGAIN ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL;
diff --git a/nss/nss_files/files-XXX.c b/nss/nss_files/files-XXX.c
index 558b2c364f..fa47dec277 100644
--- a/nss/nss_files/files-XXX.c
+++ b/nss/nss_files/files-XXX.c
@@ -39,7 +39,8 @@ 

 #define ENTNAME_r	CONCAT(ENTNAME,_r)

-#define DATAFILE	"/etc/" DATABASE
+#define DATAFILE	  "/etc/" DATABASE
+#define DATAFILE_FALLBACK "/usr/share/" DATABASE

 #ifdef NEED_H_ERRNO
 # include <netdb.h>
@@ -73,7 +74,7 @@  internal_setent (FILE **stream)

   if (*stream == NULL)
     {
-      *stream = __nss_files_fopen (DATAFILE);
+      *stream = __nss_files_fopen (DATAFILE, DATAFILE_FALLBACK);

       if (*stream == NULL)
 	status = errno == EAGAIN ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL;
@@ -89,7 +90,7 @@  internal_setent (FILE **stream)
 enum nss_status
 CONCAT(_nss_files_set,ENTNAME) (int stayopen)
 {
-  return __nss_files_data_setent (CONCAT (nss_file_, ENTNAME), DATAFILE);
+  return __nss_files_data_setent (CONCAT (nss_file_, ENTNAME), DATAFILE, DATAFILE_FALLBACK);
 }
 libc_hidden_def (CONCAT (_nss_files_set,ENTNAME))

@@ -170,7 +171,7 @@  CONCAT(_nss_files_get,ENTNAME_r) (struct STRUCTURE *result, char *buffer,
   struct nss_files_per_file_data *data;
   enum nss_status status = __nss_files_data_open (&data,
 						  CONCAT (nss_file_, ENTNAME),
-						  DATAFILE,
+						  DATAFILE, DATAFILE_FALLBACK,
 						  errnop, H_ERRNO_ARG_OR_NULL);
   if (status != NSS_STATUS_SUCCESS)
     return status;
diff --git a/nss/nss_files/files-alias.c b/nss/nss_files/files-alias.c
index 14a59b4655..d0b1ce2a8e 100644
--- a/nss/nss_files/files-alias.c
+++ b/nss/nss_files/files-alias.c
@@ -42,7 +42,7 @@  internal_setent (FILE **stream)

   if (*stream == NULL)
     {
-      *stream = __nss_files_fopen ("/etc/aliases");
+      *stream = __nss_files_fopen ("/etc/aliases", "/usr/share/aliases");

       if (*stream == NULL)
 	status = errno == EAGAIN ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL;
@@ -58,7 +58,7 @@  internal_setent (FILE **stream)
 enum nss_status
 _nss_files_setaliasent (void)
 {
-  return __nss_files_data_setent (nss_file_aliasent, "/etc/aliases");
+   return __nss_files_data_setent (nss_file_aliasent, "/etc/aliases", "/usr/share/aliases");
 }
 libc_hidden_def (_nss_files_setaliasent)

@@ -182,7 +182,7 @@  get_next_alias (FILE *stream, const char *match, struct aliasent *result,

 		      first_unused = cp;

-		      listfile = __nss_files_fopen (&cp[9]);
+		      listfile = __nss_files_fopen (&cp[9], NULL);
 		      /* If the file does not exist we simply ignore
 			 the statement.  */
 		      if (listfile != NULL
@@ -338,7 +338,8 @@  _nss_files_getaliasent_r (struct aliasent *result, char *buffer, size_t buflen,

   struct nss_files_per_file_data *data;
   enum nss_status status = __nss_files_data_open (&data, nss_file_aliasent,
-						  "/etc/aliases", errnop, NULL);
+						  "/etc/aliases", "/usr/share/aliases",
+                                                  errnop, NULL);
   if (status != NSS_STATUS_SUCCESS)
     return status;

diff --git a/nss/nss_files/files-initgroups.c b/nss/nss_files/files-initgroups.c
index 65189d3929..896fa50c5a 100644
--- a/nss/nss_files/files-initgroups.c
+++ b/nss/nss_files/files-initgroups.c
@@ -33,7 +33,7 @@  _nss_files_initgroups_dyn (const char *user, gid_t group, long int *start,
 			   long int *size, gid_t **groupsp, long int limit,
 			   int *errnop)
 {
-  FILE *stream = __nss_files_fopen ("/etc/group");
+  FILE *stream = __nss_files_fopen ("/etc/group", "/usr/share/group");
   if (stream == NULL)
     {
       *errnop = errno;
diff --git a/nss/nss_files/files-netgrp.c b/nss/nss_files/files-netgrp.c
index 92d8062e43..7f71eebf60 100644
--- a/nss/nss_files/files-netgrp.c
+++ b/nss/nss_files/files-netgrp.c
@@ -27,7 +27,8 @@ 
 #include "netgroup.h"
 #include <nss_files.h>

-#define DATAFILE	"/etc/netgroup"
+#define DATAFILE          "/etc/netgroup"
+#define DATAFILE_FALLBACK "/usr/share/netgroup"

 libc_hidden_proto (_nss_files_endnetgrent)

@@ -62,7 +63,7 @@  _nss_files_setnetgrent (const char *group, struct __netgrent *result)
     return NSS_STATUS_UNAVAIL;

   /* Find the netgroups file and open it.  */
-  fp = __nss_files_fopen (DATAFILE);
+  fp = __nss_files_fopen (DATAFILE, DATAFILE_FALLBACK);
   if (fp == NULL)
     status = errno == EAGAIN ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL;
   else
diff --git a/nss/nss_files_data.c b/nss/nss_files_data.c
index 261a01a775..c6af527e6d 100644
--- a/nss/nss_files_data.c
+++ b/nss/nss_files_data.c
@@ -74,13 +74,13 @@  __nss_files_data_get (struct nss_files_per_file_data **pdata,
 /* Helper function for opening the backing file at PATH.  */
 static enum nss_status
 __nss_files_data_internal_open (struct nss_files_per_file_data *data,
-                                const char *path)
+                                const char *path, const char *fallback_path)
 {
   enum nss_status status = NSS_STATUS_SUCCESS;

   if (data->stream == NULL)
     {
-      data->stream = __nss_files_fopen (path);
+      data->stream = __nss_files_fopen (path, fallback_path);

       if (data->stream == NULL)
         status = errno == EAGAIN ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL;
@@ -93,6 +93,7 @@  __nss_files_data_internal_open (struct nss_files_per_file_data *data,
 enum nss_status
 __nss_files_data_open (struct nss_files_per_file_data **pdata,
                        enum nss_files_file file, const char *path,
+                       const char *fallback_path,
                        int *errnop, int *herrnop)
 {
   enum nss_status status = __nss_files_data_get (pdata, file, errnop, herrnop);
@@ -103,7 +104,7 @@  __nss_files_data_open (struct nss_files_per_file_data **pdata,
   if ((*pdata)->stream == NULL)
     {
       int saved_errno = errno;
-      status = __nss_files_data_internal_open (*pdata, path);
+      status = __nss_files_data_internal_open (*pdata, path, fallback_path);
       __set_errno (saved_errno);
       if (status != NSS_STATUS_SUCCESS)
         __nss_files_data_put (*pdata);
@@ -122,7 +123,7 @@  __nss_files_data_put (struct nss_files_per_file_data *data)
 libc_hidden_def (__nss_files_data_put)

 enum nss_status
-__nss_files_data_setent (enum nss_files_file file, const char *path)
+__nss_files_data_setent (enum nss_files_file file, const char *path, const char *fallback_path)
 {
   struct nss_files_per_file_data *data;
   enum nss_status status = __nss_files_data_get (&data, file, NULL, NULL);
@@ -130,7 +131,7 @@  __nss_files_data_setent (enum nss_files_file file, const char *path)
     return status;

   if (data->stream == NULL)
-    status = __nss_files_data_internal_open (data, path);
+    status = __nss_files_data_internal_open (data, path, fallback_path);
   else
     rewind (data->stream);

diff --git a/nss/nss_files_fopen.c b/nss/nss_files_fopen.c
index e7c48d7bd8..a2b7affdb4 100644
--- a/nss/nss_files_fopen.c
+++ b/nss/nss_files_fopen.c
@@ -22,11 +22,18 @@ 
 #include <stdio_ext.h>

 FILE *
-__nss_files_fopen (const char *path)
+__nss_files_fopen (const char *path, const char *fallback_path)
 {
   FILE *fp = fopen (path, "rce");
   if (fp == NULL)
-    return NULL;
+    {
+      if (errno != ENOENT || fallback_path == NULL)
+        return NULL;
+
+      fp = fopen (fallback_path, "rce");
+      if (fp == NULL)
+        return NULL;
+    }

   /* The stream is not shared across threads.  */
   __fsetlocking (fp, FSETLOCKING_BYCALLER);