]> git.decadent.org.uk Git - nfs-utils.git/blob - utils/mount/configfile.c
d3285f8bf0cac5f9ebcdc2cbfd7609d163cdf4bf
[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
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <ctype.h>
28
29 #include "xlog.h"
30 #include "conffile.h"
31
32 #define KBYTES(x)     ((x) * (1024))
33 #define MEGABYTES(x)  ((x) * (1048576))
34 #define GIGABYTES(x)  ((x) * (1073741824))
35
36 #ifndef NFSMOUNT_GLOBAL_OPTS
37 #define NFSMOUNT_GLOBAL_OPTS "NFSMount_Global_Options"
38 #endif
39
40 #ifndef NFSMOUNT_MOUNTPOINT
41 #define NFSMOUNT_MOUNTPOINT "MountPoint"
42 #endif
43
44 #ifndef NFSMOUNT_SERVER
45 #define NFSMOUNT_SERVER "Server"
46 #endif
47
48 #ifndef MOUNTOPTS_CONFFILE
49 #define MOUNTOPTS_CONFFILE "/etc/nfsmount.conf"
50 #endif
51 char *conf_path = MOUNTOPTS_CONFFILE;
52 enum {
53         MNT_NOARG=0,
54         MNT_INTARG,
55         MNT_STRARG,
56         MNT_SPEC,
57         MNT_UNSET
58 };
59 struct mnt_alias {
60         char *alias;
61         char *opt;
62         int  argtype;
63 } mnt_alias_tab[] = {
64         {"background", "bg", MNT_NOARG},
65         {"foreground", "fg", MNT_NOARG},
66         {"sloppy", "sloppy", MNT_NOARG},
67 };
68 int mnt_alias_sz = (sizeof(mnt_alias_tab)/sizeof(mnt_alias_tab[0]));
69
70 /*
71  * See if the option is an alias, if so return the 
72  * real mount option along with the argument type.
73  */
74 inline static 
75 char *mountopts_alias(char *opt, int *argtype)
76 {
77         int i;
78
79         *argtype = MNT_UNSET;
80         for (i=0; i < mnt_alias_sz; i++) {
81                 if (strcasecmp(opt, mnt_alias_tab[i].alias) != 0)
82                         continue;
83                 *argtype = mnt_alias_tab[i].argtype;
84                 return mnt_alias_tab[i].opt;
85         }
86         /* Make option names case-insensitive */
87         upper2lower(opt);
88
89         return opt;
90 }
91 /*
92  * Convert numeric strings that end with 'k', 'm' or 'g'
93  * into numeric strings with the real value. 
94  * Meaning '8k' becomes '8094'.
95  */
96 char *mountopts_convert(char *value)
97 {
98         unsigned long long factor, num;
99         static char buf[64];
100         char *ch;
101
102         ch = &value[strlen(value)-1];
103         switch (tolower(*ch)) {
104         case 'k':
105                 factor = KBYTES(1);
106                 break;
107         case 'm':
108                 factor = MEGABYTES(1);
109                 break;
110         case 'g':
111                 factor = GIGABYTES(1);
112                 break;
113         default:
114                 return value;
115         }
116         *ch = '\0';
117         if (strncmp(value, "0x", 2) == 0) {
118                 num = strtol(value, (char **)NULL, 16);
119         } else if (strncmp(value, "0", 1) == 0) {
120                 num = strtol(value, (char **)NULL, 8);
121         } else {
122                 num = strtol(value, (char **)NULL, 10);
123         }
124         num *= factor;
125         snprintf(buf, 64, "%lld", num);
126
127         return buf;
128 }
129
130 struct entry {
131         SLIST_ENTRY(entry) entries;
132         char *opt;
133 };
134 static SLIST_HEAD(shead, entry) head = SLIST_HEAD_INITIALIZER(head);
135 static int list_size;
136
137 /*
138  * Add option to the link list
139  */
140 inline static void 
141 add_entry(char *opt)
142 {
143         struct entry *entry;
144
145         entry = calloc(1, sizeof(struct entry));
146         if (entry == NULL) {
147                 xlog_warn("Unable calloc memory for mount configs"); 
148                 return;
149         }
150         entry->opt = strdup(opt);
151         if (entry->opt == NULL) {
152                 xlog_warn("Unable calloc memory for mount opts"); 
153                 free(entry);
154                 return;
155         }
156         SLIST_INSERT_HEAD(&head, entry, entries);
157 }
158 /*
159  * See if the given entry exists if the link list,
160  * if so return that entry
161  */
162 inline static 
163 char *lookup_entry(char *opt)
164 {
165         struct entry *entry;
166
167         SLIST_FOREACH(entry, &head, entries) {
168                 if (strcasecmp(entry->opt, opt) == 0)
169                         return opt;
170         }
171         return NULL;
172 }
173 /*
174  * Free all entries on the link list
175  */
176 inline static 
177 void free_all(void)
178 {
179         struct entry *entry;
180
181         while (!SLIST_EMPTY(&head)) {
182                 entry = SLIST_FIRST(&head);
183                 SLIST_REMOVE_HEAD(&head, entries);
184                 free(entry->opt);
185                 free(entry);
186         }
187 }
188 static char *versions[] = {"v2", "v3", "v4", "vers", "nfsvers", NULL};
189 int inline check_vers(char *mopt, char *field)
190 {
191         int i;
192
193         if (strncmp("mountvers", field, strlen("mountvers")) != 0) {
194                 for (i=0; versions[i]; i++) 
195                         if (strcasestr(mopt, versions[i]) != NULL)
196                                 return 1;
197         }
198         return 0;
199 }
200 /*
201  * Parse the given section of the configuration 
202  * file to if there are any mount options set.
203  * If so, added them to link list.
204  */
205 static void 
206 conf_parse_mntopts(char *section, char *arg, char *opts)
207 {
208         struct conf_list *list;
209         struct conf_list_node *node;
210         char buf[BUFSIZ], *value, *field;
211         char *nvalue, *ptr;
212         int argtype;
213
214         list = conf_get_tag_list(section);
215         TAILQ_FOREACH(node, &list->fields, link) {
216                 /*
217                  * Do not overwrite options if already exists 
218                  */
219                 snprintf(buf, BUFSIZ, "%s=", node->field);
220                 if (opts && strcasestr(opts, buf) != NULL)
221                         continue;
222                 /* 
223                  * Protocol verions can be set in a number of ways
224                  */
225                 if (opts && check_vers(opts, node->field))
226                         continue;
227
228                 if (lookup_entry(node->field) != NULL)
229                         continue;
230                 buf[0] = '\0';
231                 value = conf_get_section(section, arg, node->field);
232                 if (value == NULL)
233                         continue;
234                 field = mountopts_alias(node->field, &argtype);
235                 if (strcasecmp(value, "false") == 0) {
236                         if (argtype != MNT_NOARG)
237                                 snprintf(buf, BUFSIZ, "no%s", field);
238                 } else if (strcasecmp(value, "true") == 0) {
239                         snprintf(buf, BUFSIZ, "%s", field);
240                 } else {
241                         nvalue = strdup(value);
242                         ptr = mountopts_convert(nvalue);
243                         snprintf(buf, BUFSIZ, "%s=%s", field, ptr);
244                         free(nvalue);
245                 }
246                 if (buf[0] == '\0')
247                         continue;
248                 /* 
249                  * Keep a running tally of the list size adding 
250                  * one for the ',' that will be appened later
251                  */
252                 list_size += strlen(buf) + 1;
253                 add_entry(buf);
254         }
255         conf_free_list(list);
256 }
257
258 /*
259  * Concatenate options from the configuration file with the 
260  * given options by building a link list of options from the
261  * different sections in the conf file. Options that exists 
262  * in the either the given options or link list are not 
263  * overwritten so it matter which when each section is
264  * parsed. 
265  */
266 char *conf_get_mntopts(char *spec, char *mount_point, 
267         char *mount_opts)
268 {
269         struct entry *entry;
270         char *ptr, *server, *config_opts;
271         int optlen = 0;
272
273         SLIST_INIT(&head);
274         list_size = 0;
275         /*
276          * First see if there are any mount options relative 
277          * to the mount point.
278          */
279         conf_parse_mntopts(NFSMOUNT_MOUNTPOINT, mount_point, mount_opts);
280
281         /* 
282          * Next, see if there are any mount options relative
283          * to the server
284          */
285         server = strdup(spec);
286         if (server == NULL) {
287                 xlog_warn("conf_get_mountops: Unable calloc memory for server"); 
288                 free_all();
289                 return mount_opts;
290         }
291         if ((ptr = strchr(server, ':')) != NULL)
292                 *ptr='\0';
293         conf_parse_mntopts(NFSMOUNT_SERVER, server, mount_opts);
294         free(server);
295
296         /*
297          * Finally process all the global mount options. 
298          */
299         conf_parse_mntopts(NFSMOUNT_GLOBAL_OPTS, NULL, mount_opts);
300
301         /*
302          * If no mount options were found in the configuration file
303          * just return what was passed in .
304          */
305         if (SLIST_EMPTY(&head))
306                 return mount_opts;
307
308         /*
309          * Found options in the configuration file. So
310          * concatenate the configuration options with the 
311          * options that were passed in
312          */
313         if (mount_opts)
314                 optlen = strlen(mount_opts);
315
316         /* list_size + optlen + ',' + '\0' */
317         config_opts = calloc(1, (list_size+optlen+2));
318         if (server == NULL) {
319                 xlog_warn("conf_get_mountops: Unable calloc memory for config_opts"); 
320                 free_all();
321                 return mount_opts;
322         }
323         if (mount_opts) {
324                 strcpy(config_opts, mount_opts);
325                 strcat(config_opts, ",");
326         }
327         SLIST_FOREACH(entry, &head, entries) {
328                 strcat(config_opts, entry->opt);
329                 strcat(config_opts, ",");
330         }
331         *(strrchr(config_opts, ',')) = '\0';
332
333         free_all();
334         if (mount_opts)
335                 free(mount_opts);
336
337         return config_opts;
338 }