1 /* Returns a pointer to the global nss_files data structure.
2    Copyright (C) 2021 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4 
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9 
10    The GNU C Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14 
15    You should have received a copy of the GNU Lesser General Public
16    License along with the GNU C Library; if not, see
17    <https://www.gnu.org/licenses/>.  */
18 
19 #include <nss_files.h>
20 
21 #include <allocate_once.h>
22 #include <errno.h>
23 #include <netdb.h>
24 #include <nss.h>
25 #include <stdlib.h>
26 
27 /* This collects all per file-data.   */
28 struct nss_files_data
29 {
30   struct nss_files_per_file_data files[nss_file_count];
31 };
32 
33 /* For use with allocate_once.  */
34 static void *nss_files_global;
35 static void *
nss_files_global_allocate(void * closure)36 nss_files_global_allocate (void *closure)
37 {
38   struct nss_files_data *result = malloc (sizeof (*result));
39   if (result != NULL)
40     {
41       for (int i = 0; i < nss_file_count; ++i)
42         {
43           result->files[i].stream = NULL;
44           __libc_lock_init (result->files[i].lock);
45         }
46     }
47   return result;
48 }
49 /* Like __nss_files_data_open, but does not perform the open call.  */
50 static enum nss_status
__nss_files_data_get(struct nss_files_per_file_data ** pdata,enum nss_files_file file,int * errnop,int * herrnop)51 __nss_files_data_get (struct nss_files_per_file_data **pdata,
52                       enum nss_files_file file, int *errnop, int *herrnop)
53 {
54   struct nss_files_data *data = allocate_once (&nss_files_global,
55                                                nss_files_global_allocate,
56                                                NULL, NULL);
57   if (data == NULL)
58     {
59       if (errnop != NULL)
60         *errnop = errno;
61       if (herrnop != NULL)
62         {
63           __set_h_errno (NETDB_INTERNAL);
64           *herrnop = NETDB_INTERNAL;
65         }
66       return NSS_STATUS_TRYAGAIN;
67     }
68 
69   *pdata = &data->files[file];
70   __libc_lock_lock ((*pdata)->lock);
71   return NSS_STATUS_SUCCESS;
72 }
73 
74 /* Helper function for opening the backing file at PATH.  */
75 static enum nss_status
__nss_files_data_internal_open(struct nss_files_per_file_data * data,const char * path)76 __nss_files_data_internal_open (struct nss_files_per_file_data *data,
77                                 const char *path)
78 {
79   enum nss_status status = NSS_STATUS_SUCCESS;
80 
81   if (data->stream == NULL)
82     {
83       data->stream = __nss_files_fopen (path);
84 
85       if (data->stream == NULL)
86         status = errno == EAGAIN ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL;
87     }
88 
89   return status;
90 }
91 
92 
93 enum nss_status
__nss_files_data_open(struct nss_files_per_file_data ** pdata,enum nss_files_file file,const char * path,int * errnop,int * herrnop)94 __nss_files_data_open (struct nss_files_per_file_data **pdata,
95                        enum nss_files_file file, const char *path,
96                        int *errnop, int *herrnop)
97 {
98   enum nss_status status = __nss_files_data_get (pdata, file, errnop, herrnop);
99   if (status != NSS_STATUS_SUCCESS)
100     return status;
101 
102   /* Be prepared that the set*ent function was not called before.  */
103   if ((*pdata)->stream == NULL)
104     {
105       int saved_errno = errno;
106       status = __nss_files_data_internal_open (*pdata, path);
107       __set_errno (saved_errno);
108       if (status != NSS_STATUS_SUCCESS)
109         __nss_files_data_put (*pdata);
110     }
111 
112   return status;
113 }
114 
libc_hidden_def(__nss_files_data_open)115 libc_hidden_def (__nss_files_data_open)
116 
117 void
118 __nss_files_data_put (struct nss_files_per_file_data *data)
119 {
120   __libc_lock_unlock (data->lock);
121 }
libc_hidden_def(__nss_files_data_put)122 libc_hidden_def (__nss_files_data_put)
123 
124 enum nss_status
125 __nss_files_data_setent (enum nss_files_file file, const char *path)
126 {
127   struct nss_files_per_file_data *data;
128   enum nss_status status = __nss_files_data_get (&data, file, NULL, NULL);
129   if (status != NSS_STATUS_SUCCESS)
130     return status;
131 
132   if (data->stream == NULL)
133     status = __nss_files_data_internal_open (data, path);
134   else
135     rewind (data->stream);
136 
137   __nss_files_data_put (data);
138   return status;
139 }
libc_hidden_def(__nss_files_data_setent)140 libc_hidden_def (__nss_files_data_setent)
141 
142 enum nss_status
143 __nss_files_data_endent (enum nss_files_file file)
144 {
145   /* No cleanup is necessary if not initialized.  */
146   struct nss_files_data *data = atomic_load_acquire (&nss_files_global);
147   if (data == NULL)
148     return NSS_STATUS_SUCCESS;
149 
150   struct nss_files_per_file_data *fdata = &data->files[file];
151   __libc_lock_lock (fdata->lock);
152   if (fdata->stream != NULL)
153     {
154       fclose (fdata->stream);
155       fdata->stream = NULL;
156     }
157   __libc_lock_unlock (fdata->lock);
158 
159   return NSS_STATUS_SUCCESS;
160 }
161 libc_hidden_def (__nss_files_data_endent)
162