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