export: Ensure that we free struct exportent->e_uuid
[nfs-utils.git] / support / export / export.c
1 /*
2  * support/export/export.c
3  *
4  * Maintain list of exported file systems.
5  *
6  * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
7  */
8
9 #ifdef HAVE_CONFIG_H
10 #include <config.h>
11 #endif
12
13 #include <string.h>
14 #include <sys/types.h>
15 #include <sys/param.h>
16 #include <netinet/in.h>
17 #include <stdlib.h>
18 #include "xmalloc.h"
19 #include "nfslib.h"
20 #include "exportfs.h"
21
22 exp_hash_table exportlist[MCL_MAXTYPES] = {{NULL, {{NULL,NULL}, }}, }; 
23 static int export_hash(char *);
24
25 static void     export_init(nfs_export *exp, nfs_client *clp,
26                                         struct exportent *nep);
27 static void     export_add(nfs_export *exp);
28 static int      export_check(const nfs_export *exp, const struct addrinfo *ai,
29                                 const char *path);
30 static nfs_export *
31                 export_allowed_internal(const struct addrinfo *ai,
32                                 const char *path);
33
34 static void
35 export_free(nfs_export *exp)
36 {
37         xfree(exp->m_export.e_squids);
38         xfree(exp->m_export.e_sqgids);
39         free(exp->m_export.e_mountpoint);
40         free(exp->m_export.e_fslocdata);
41         free(exp->m_export.e_uuid);
42
43         xfree(exp->m_export.e_hostname);
44         xfree(exp);
45 }
46
47 static void warn_duplicated_exports(nfs_export *exp, struct exportent *eep)
48 {
49         if (exp->m_export.e_flags != eep->e_flags) {
50                 xlog(L_ERROR, "incompatible duplicated export entries:");
51                 xlog(L_ERROR, "\t%s:%s (0x%x) [IGNORED]", eep->e_hostname,
52                                 eep->e_path, eep->e_flags);
53                 xlog(L_ERROR, "\t%s:%s (0x%x)", exp->m_export.e_hostname,
54                                 exp->m_export.e_path, exp->m_export.e_flags);
55         } else {
56                 xlog(L_ERROR, "duplicated export entries:");
57                 xlog(L_ERROR, "\t%s:%s", eep->e_hostname, eep->e_path);
58                 xlog(L_ERROR, "\t%s:%s", exp->m_export.e_hostname,
59                                 exp->m_export.e_path);
60         }
61 }
62
63 /**
64  * export_read - read entries from /etc/exports
65  * @fname: name of file to read from
66  *
67  */
68 void
69 export_read(char *fname)
70 {
71         struct exportent        *eep;
72         nfs_export              *exp;
73
74         setexportent(fname, "r");
75         while ((eep = getexportent(0,1)) != NULL) {
76                 exp = export_lookup(eep->e_hostname, eep->e_path, 0);
77                 if (!exp)
78                         export_create(eep, 0);
79                 else
80                         warn_duplicated_exports(exp, eep);
81         }
82         endexportent();
83 }
84
85 /**
86  * export_create - create an in-core nfs_export record from an export entry
87  * @xep: export entry to lookup
88  * @canonical: if set, e_hostname is known to be canonical DNS name
89  *
90  * Returns a freshly instantiated export record, or NULL if
91  * a problem occurred.
92  */
93 nfs_export *
94 export_create(struct exportent *xep, int canonical)
95 {
96         nfs_client      *clp;
97         nfs_export      *exp;
98
99         if (!(clp = client_lookup(xep->e_hostname, canonical))) {
100                 /* bad export entry; complaint already logged */
101                 return NULL;
102         }
103         exp = (nfs_export *) xmalloc(sizeof(*exp));
104         export_init(exp, clp, xep);
105         export_add(exp);
106
107         return exp;
108 }
109
110 static void
111 export_init(nfs_export *exp, nfs_client *clp, struct exportent *nep)
112 {
113         struct exportent        *e = &exp->m_export;
114
115         dupexportent(e, nep);
116         if (nep->e_hostname)
117                 e->e_hostname = xstrdup(nep->e_hostname);
118
119         exp->m_exported = 0;
120         exp->m_xtabent = 0;
121         exp->m_mayexport = 0;
122         exp->m_changed = 0;
123         exp->m_warned = 0;
124         exp->m_client = clp;
125         clp->m_count++;
126 }
127
128 /*
129  * Duplicate exports data. The in-core export struct retains the
130  * original hostname from /etc/exports, while the in-core client struct
131  * gets the newly found FQDN.
132  */
133 static nfs_export *
134 export_dup(nfs_export *exp, const struct addrinfo *ai)
135 {
136         nfs_export              *new;
137         nfs_client              *clp;
138
139         new = (nfs_export *) xmalloc(sizeof(*new));
140         memcpy(new, exp, sizeof(*new));
141         dupexportent(&new->m_export, &exp->m_export);
142         if (exp->m_export.e_hostname)
143                 new->m_export.e_hostname = xstrdup(exp->m_export.e_hostname);
144         clp = client_dup(exp->m_client, ai);
145         if (clp == NULL) {
146                 export_free(new);
147                 return NULL;
148         }
149         clp->m_count++;
150         new->m_client = clp;
151         new->m_mayexport = exp->m_mayexport;
152         new->m_exported = 0;
153         new->m_xtabent = 0;
154         new->m_changed = 0;
155         new->m_warned = 0;
156         export_add(new);
157
158         return new;
159 }
160
161 static void
162 export_add(nfs_export *exp)
163 {
164         exp_hash_table *p_tbl;
165         exp_hash_entry *p_hen;
166         nfs_export *p_next;
167
168         int type = exp->m_client->m_type;
169         int pos;
170
171         pos = export_hash(exp->m_export.e_path);
172         p_tbl = &(exportlist[type]); /* pointer to hash table */
173         p_hen = &(p_tbl->entries[pos]); /* pointer to hash table entry */
174
175         if (!(p_hen->p_first)) { /* hash table entry is empty */ 
176                 p_hen->p_first = exp;
177                 p_hen->p_last  = exp;
178
179                 exp->m_next = p_tbl->p_head;
180                 p_tbl->p_head = exp;
181         } else { /* hash table entry is NOT empty */
182                 p_next = p_hen->p_last->m_next;
183                 p_hen->p_last->m_next = exp;
184                 exp->m_next = p_next;
185                 p_hen->p_last = exp;
186         }
187 }
188
189 /**
190  * export_find - find or create a suitable nfs_export for @ai and @path
191  * @ai: pointer to addrinfo for client
192  * @path: '\0'-terminated ASCII string containing export path
193  *
194  * Returns a pointer to nfs_export data matching @ai and @path,
195  * or NULL if an error occurs.
196  */
197 nfs_export *
198 export_find(const struct addrinfo *ai, const char *path)
199 {
200         nfs_export      *exp;
201         int             i;
202
203         for (i = 0; i < MCL_MAXTYPES; i++) {
204                 for (exp = exportlist[i].p_head; exp; exp = exp->m_next) {
205                         if (!export_check(exp, ai, path))
206                                 continue;
207                         if (exp->m_client->m_type == MCL_FQDN)
208                                 return exp;
209                         return export_dup(exp, ai);
210                 }
211         }
212
213         return NULL;
214 }
215
216 static nfs_export *
217 export_allowed_internal(const struct addrinfo *ai, const char *path)
218 {
219         nfs_export      *exp;
220         int             i;
221
222         for (i = 0; i < MCL_MAXTYPES; i++) {
223                 for (exp = exportlist[i].p_head; exp; exp = exp->m_next) {
224                         if (!exp->m_mayexport ||
225                             !export_check(exp, ai, path))
226                                 continue;
227                         return exp;
228                 }
229         }
230
231         return NULL;
232 }
233
234 /**
235  * export_allowed - determine if this export is allowed
236  * @ai: pointer to addrinfo for client
237  * @path: '\0'-terminated ASCII string containing export path
238  *
239  * Returns a pointer to nfs_export data matching @ai and @path,
240  * or NULL if the export is not allowed.
241  */
242 nfs_export *
243 export_allowed(const struct addrinfo *ai, const char *path)
244 {
245         nfs_export              *exp;
246         char                    epath[MAXPATHLEN+1];
247         char                    *p = NULL;
248
249         if (path [0] != '/') return NULL;
250
251         strncpy(epath, path, sizeof (epath) - 1);
252         epath[sizeof (epath) - 1] = '\0';
253
254         /* Try the longest matching exported pathname. */
255         while (1) {
256                 exp = export_allowed_internal(ai, epath);
257                 if (exp)
258                         return exp;
259                 /* We have to treat the root, "/", specially. */
260                 if (p == &epath[1]) break;
261                 p = strrchr(epath, '/');
262                 if (p == epath) p++;
263                 *p = '\0';
264         }
265
266         return NULL;
267 }
268
269 /**
270  * export_lookup - search hash table for export entry
271  * @hname: '\0'-terminated ASCII string containing client hostname to look for
272  * @path: '\0'-terminated ASCII string containing export path to look for
273  * @canonical: if set, @hname is known to be canonical DNS name
274  *
275  * Returns a pointer to nfs_export record matching @hname and @path,
276  * or NULL if the export was not found.
277  */
278 nfs_export *
279 export_lookup(char *hname, char *path, int canonical)
280 {
281         nfs_client *clp;
282         nfs_export *exp;
283         exp_hash_entry *p_hen;
284
285         int pos;
286
287         clp = client_lookup(hname, canonical);
288         if(clp == NULL)
289                 return NULL;
290
291         pos = export_hash(path);
292         p_hen = &(exportlist[clp->m_type].entries[pos]); 
293         for(exp = p_hen->p_first; exp && (exp != p_hen->p_last->m_next); 
294                         exp = exp->m_next) {
295                 if (exp->m_client == clp && !strcmp(exp->m_export.e_path, path)) {
296                         return exp;
297                 }
298         }
299         return NULL;
300 }
301
302 static int
303 export_check(const nfs_export *exp, const struct addrinfo *ai, const char *path)
304 {
305         if (strcmp(path, exp->m_export.e_path))
306                 return 0;
307
308         return client_check(exp->m_client, ai);
309 }
310
311 /**
312  * export_freeall - deallocate all nfs_export records
313  *
314  */
315 void
316 export_freeall(void)
317 {
318         nfs_export      *exp, *nxt;
319         int             i, j;
320
321         for (i = 0; i < MCL_MAXTYPES; i++) {
322                 for (exp = exportlist[i].p_head; exp; exp = nxt) {
323                         nxt = exp->m_next;
324                         client_release(exp->m_client);
325                         export_free(exp);
326                 }
327                 for (j = 0; j < HASH_TABLE_SIZE; j++) {
328                         exportlist[i].entries[j].p_first = NULL;
329                         exportlist[i].entries[j].p_last = NULL;
330                 }
331                 exportlist[i].p_head = NULL;
332         }
333         client_freeall();
334 }
335
336 /*
337  * Compute and returns integer from string. 
338  * Note: Its understood the smae integers can be same for 
339  *       different strings, but it should not matter.
340  */
341 static unsigned int 
342 strtoint(char *str)
343 {
344         int i = 0;
345         unsigned int n = 0;
346
347         while ( str[i] != '\0') {
348                 n+=((int)str[i])*i;
349                 i++;
350         }
351         return n;
352 }
353
354 /*
355  * Hash function
356  */
357 static int 
358 export_hash(char *str)
359 {
360         int num = strtoint(str);
361
362         return num % HASH_TABLE_SIZE;
363 }