]> git.decadent.org.uk Git - nfs-utils.git/blob - utils/mount/configfile.c
mount.nfs: Assume v2/v3 if mount-related options are present
[nfs-utils.git] / utils / mount / configfile.c
1 /*
2  * configfile.c -- mount configuration file manipulation 
3  * Copyright (C) 2008 Red Hat, Inc <nfs@redhat.com>
4  *
5  * - Routines use to create mount options from the mount
6  *   configuration file.
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2, or (at your option)
11  * any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  */
19 #ifdef HAVE_CONFIG_H
20 #include <config.h>
21 #endif
22 #include <sys/types.h>
23 #include <sys/socket.h>
24 #include <netinet/in.h>
25
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <ctype.h>
30 #include <errno.h>
31
32 #include "xlog.h"
33 #include "mount.h"
34 #include "parse_opt.h"
35 #include "network.h"
36 #include "conffile.h"
37
38 #define KBYTES(x)     ((x) * (1024))
39 #define MEGABYTES(x)  ((x) * (1048576))
40 #define GIGABYTES(x)  ((x) * (1073741824))
41
42 #ifndef NFSMOUNT_GLOBAL_OPTS
43 #define NFSMOUNT_GLOBAL_OPTS "NFSMount_Global_Options"
44 #endif
45
46 #ifndef NFSMOUNT_MOUNTPOINT
47 #define NFSMOUNT_MOUNTPOINT "MountPoint"
48 #endif
49
50 #ifndef NFSMOUNT_SERVER
51 #define NFSMOUNT_SERVER "Server"
52 #endif
53
54 #ifndef MOUNTOPTS_CONFFILE
55 #define MOUNTOPTS_CONFFILE "/etc/nfsmount.conf"
56 #endif
57 char *conf_path = MOUNTOPTS_CONFFILE;
58 enum {
59         MNT_NOARG=0,
60         MNT_INTARG,
61         MNT_STRARG,
62         MNT_SPEC,
63         MNT_UNSET
64 };
65 struct mnt_alias {
66         char *alias;
67         char *opt;
68         int  argtype;
69 } mnt_alias_tab[] = {
70         {"background", "bg", MNT_NOARG},
71         {"foreground", "fg", MNT_NOARG},
72         {"sloppy", "sloppy", MNT_NOARG},
73 };
74 int mnt_alias_sz = (sizeof(mnt_alias_tab)/sizeof(mnt_alias_tab[0]));
75
76 /*
77  * See if the option is an alias, if so return the 
78  * real mount option along with the argument type.
79  */
80 inline static 
81 char *mountopts_alias(char *opt, int *argtype)
82 {
83         int i;
84
85         *argtype = MNT_UNSET;
86         for (i=0; i < mnt_alias_sz; i++) {
87                 if (strcasecmp(opt, mnt_alias_tab[i].alias) != 0)
88                         continue;
89                 *argtype = mnt_alias_tab[i].argtype;
90                 return mnt_alias_tab[i].opt;
91         }
92         /* Make option names case-insensitive */
93         upper2lower(opt);
94
95         return opt;
96 }
97 /*
98  * Convert numeric strings that end with 'k', 'm' or 'g'
99  * into numeric strings with the real value. 
100  * Meaning '8k' becomes '8094'.
101  */
102 char *mountopts_convert(char *value)
103 {
104         unsigned long long factor, num;
105         static char buf[64];
106         char *ch;
107
108         ch = &value[strlen(value)-1];
109         switch (tolower(*ch)) {
110         case 'k':
111                 factor = KBYTES(1);
112                 break;
113         case 'm':
114                 factor = MEGABYTES(1);
115                 break;
116         case 'g':
117                 factor = GIGABYTES(1);
118                 break;
119         default:
120                 return value;
121         }
122         *ch = '\0';
123         if (strncmp(value, "0x", 2) == 0) {
124                 num = strtol(value, (char **)NULL, 16);
125         } else if (strncmp(value, "0", 1) == 0) {
126                 num = strtol(value, (char **)NULL, 8);
127         } else {
128                 num = strtol(value, (char **)NULL, 10);
129         }
130         num *= factor;
131         snprintf(buf, 64, "%lld", num);
132
133         return buf;
134 }
135
136 struct entry {
137         SLIST_ENTRY(entry) entries;
138         char *opt;
139 };
140 static SLIST_HEAD(shead, entry) head = SLIST_HEAD_INITIALIZER(head);
141 static int list_size;
142
143 /*
144  * Add option to the link list
145  */
146 inline static void 
147 add_entry(char *opt)
148 {
149         struct entry *entry;
150
151         entry = calloc(1, sizeof(struct entry));
152         if (entry == NULL) {
153                 xlog_warn("Unable calloc memory for mount configs"); 
154                 return;
155         }
156         entry->opt = strdup(opt);
157         if (entry->opt == NULL) {
158                 xlog_warn("Unable calloc memory for mount opts"); 
159                 free(entry);
160                 return;
161         }
162         SLIST_INSERT_HEAD(&head, entry, entries);
163 }
164 /*
165  * See if the given entry exists if the link list,
166  * if so return that entry
167  */
168 inline static 
169 char *lookup_entry(char *opt)
170 {
171         struct entry *entry;
172
173         SLIST_FOREACH(entry, &head, entries) {
174                 if (strcasecmp(entry->opt, opt) == 0)
175                         return opt;
176         }
177         return NULL;
178 }
179 /*
180  * Free all entries on the link list
181  */
182 inline static 
183 void free_all(void)
184 {
185         struct entry *entry;
186
187         while (!SLIST_EMPTY(&head)) {
188                 entry = SLIST_FIRST(&head);
189                 SLIST_REMOVE_HEAD(&head, entries);
190                 free(entry->opt);
191                 free(entry);
192         }
193 }
194 static char *versions[] = {"v2", "v3", "v4", "vers", "nfsvers", NULL};
195 int inline check_vers(char *mopt, char *field)
196 {
197         int i;
198
199         if (strncmp("mountvers", field, strlen("mountvers")) != 0) {
200                 for (i=0; versions[i]; i++) 
201                         if (strcasestr(mopt, versions[i]) != NULL)
202                                 return 1;
203         }
204         return 0;
205 }
206
207 unsigned long config_default_vers;
208 unsigned long config_default_proto;
209 /*
210  * Check to see if a default value is being set.
211  * If so, set the appropriate global value which will 
212  * be used as the initial value in the server negation.
213  */
214 int inline default_value(char *mopt)
215 {
216         struct mount_options *options = NULL;
217         int dftlen = strlen("default");
218         char *field;
219
220         if (strncasecmp(mopt, "default", dftlen) != 0)
221                 return 0;
222
223         field = mopt + dftlen;
224         if (strncasecmp(field, "proto", strlen("proto")) == 0) {
225                 if ((options = po_split(field)) != NULL) {
226                         if (!nfs_nfs_protocol(options, &config_default_proto)) {
227                                 xlog_warn("Unable to set default protocol : %s", 
228                                         strerror(errno));
229                         }
230                 } else {
231                         xlog_warn("Unable to alloc memory for default protocol");
232                 }
233         } else if (strncasecmp(field, "vers", strlen("vers")) == 0) {
234                 if ((options = po_split(field)) != NULL) {
235                         if (!nfs_nfs_version(options, &config_default_vers)) {
236                                 xlog_warn("Unable to set default version: %s", 
237                                         strerror(errno));
238                                 
239                         }
240                 } else {
241                         xlog_warn("Unable to alloc memory for default version");
242                 }
243         } else 
244                 xlog_warn("Invalid default setting: '%s'", mopt);
245
246         if (options)
247                 po_destroy(options);
248
249         return 1;
250 }
251 /*
252  * Parse the given section of the configuration 
253  * file to if there are any mount options set.
254  * If so, added them to link list.
255  */
256 static void 
257 conf_parse_mntopts(char *section, char *arg, char *opts)
258 {
259         struct conf_list *list;
260         struct conf_list_node *node;
261         char buf[BUFSIZ], *value, *field;
262         char *nvalue, *ptr;
263         int argtype;
264
265         list = conf_get_tag_list(section);
266         TAILQ_FOREACH(node, &list->fields, link) {
267                 /*
268                  * Do not overwrite options if already exists 
269                  */
270                 snprintf(buf, BUFSIZ, "%s=", node->field);
271                 if (opts && strcasestr(opts, buf) != NULL)
272                         continue;
273                 /* 
274                  * Protocol verions can be set in a number of ways
275                  */
276                 if (opts && check_vers(opts, node->field))
277                         continue;
278
279                 if (lookup_entry(node->field) != NULL)
280                         continue;
281                 buf[0] = '\0';
282                 value = conf_get_section(section, arg, node->field);
283                 if (value == NULL)
284                         continue;
285                 field = mountopts_alias(node->field, &argtype);
286                 if (strcasecmp(value, "false") == 0) {
287                         if (argtype != MNT_NOARG)
288                                 snprintf(buf, BUFSIZ, "no%s", field);
289                 } else if (strcasecmp(value, "true") == 0) {
290                         snprintf(buf, BUFSIZ, "%s", field);
291                 } else {
292                         nvalue = strdup(value);
293                         ptr = mountopts_convert(nvalue);
294                         snprintf(buf, BUFSIZ, "%s=%s", field, ptr);
295                         free(nvalue);
296                 }
297                 if (buf[0] == '\0')
298                         continue;
299                 /* 
300                  * Keep a running tally of the list size adding 
301                  * one for the ',' that will be appened later
302                  */
303                 list_size += strlen(buf) + 1;
304                 add_entry(buf);
305         }
306         conf_free_list(list);
307 }
308
309 /*
310  * Concatenate options from the configuration file with the 
311  * given options by building a link list of options from the
312  * different sections in the conf file. Options that exists 
313  * in the either the given options or link list are not 
314  * overwritten so it matter which when each section is
315  * parsed. 
316  */
317 char *conf_get_mntopts(char *spec, char *mount_point, 
318         char *mount_opts)
319 {
320         struct entry *entry;
321         char *ptr, *server, *config_opts;
322         int optlen = 0;
323
324         SLIST_INIT(&head);
325         list_size = 0;
326         /*
327          * First see if there are any mount options relative 
328          * to the mount point.
329          */
330         conf_parse_mntopts(NFSMOUNT_MOUNTPOINT, mount_point, mount_opts);
331
332         /* 
333          * Next, see if there are any mount options relative
334          * to the server
335          */
336         server = strdup(spec);
337         if (server == NULL) {
338                 xlog_warn("conf_get_mountops: Unable calloc memory for server"); 
339                 free_all();
340                 return mount_opts;
341         }
342         if ((ptr = strchr(server, ':')) != NULL)
343                 *ptr='\0';
344         conf_parse_mntopts(NFSMOUNT_SERVER, server, mount_opts);
345         free(server);
346
347         /*
348          * Finally process all the global mount options. 
349          */
350         conf_parse_mntopts(NFSMOUNT_GLOBAL_OPTS, NULL, mount_opts);
351
352         /*
353          * If no mount options were found in the configuration file
354          * just return what was passed in .
355          */
356         if (SLIST_EMPTY(&head))
357                 return mount_opts;
358
359         /*
360          * Found options in the configuration file. So
361          * concatenate the configuration options with the 
362          * options that were passed in
363          */
364         if (mount_opts)
365                 optlen = strlen(mount_opts);
366
367         /* list_size + optlen + ',' + '\0' */
368         config_opts = calloc(1, (list_size+optlen+2));
369         if (server == NULL) {
370                 xlog_warn("conf_get_mountops: Unable calloc memory for config_opts"); 
371                 free_all();
372                 return mount_opts;
373         }
374
375         if (mount_opts) {
376                 strcpy(config_opts, mount_opts);
377                 strcat(config_opts, ",");
378         }
379         SLIST_FOREACH(entry, &head, entries) {
380                 if (default_value(entry->opt))
381                         continue;
382                 strcat(config_opts, entry->opt);
383                 strcat(config_opts, ",");
384         }
385         if ((ptr = strrchr(config_opts, ',')) != NULL)
386                 *ptr = '\0';
387
388         free_all();
389         if (mount_opts)
390                 free(mount_opts);
391
392         return config_opts;
393 }