]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/mntent_cache.c
Tweak BSD mntinfo handling a bit as it seems all BSD 4.4 derived version use struct...
[bacula/bacula] / bacula / src / lib / mntent_cache.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2009-2010 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from
7    many others, a complete list can be found in the file AUTHORS.
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version three of the GNU Affero General Public
10    License as published by the Free Software Foundation and included
11    in the file LICENSE.
12
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16    General Public License for more details.
17
18    You should have received a copy of the GNU Affero General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22
23    Bacula® is a registered trademark of Kern Sibbald.
24    The licensor of Bacula is the Free Software Foundation Europe
25    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26    Switzerland, email:ftf@fsfeurope.org.
27 */
28
29 /**
30  * This code implements a cache with the current mounted filesystems for which
31  * its uses the mostly in kernel mount information and export the different OS
32  * specific interfaces using a generic interface. We use a hashed cache which is
33  * accessed using a hash on the device id and we keep the previous cache hit as
34  * most of the time we get called quite a lot with most of the time the same
35  * device so keeping the previous cache hit we have a very optimized code path.
36  *
37  * This interface is implemented for the following OS-es:
38  *
39  * - Linux
40  * - HPUX
41  * - DARWIN (OSX)
42  * - IRIX
43  * - AIX
44  * - OSF1 (True64)
45  * - Solaris
46  *
47  * Currently we only use this code for Linux and OSF1 based fstype determination.
48  * For the other OS-es we can use the fstype present in stat structure on those OS-es.
49  *
50  * This code replaces the big switch we used before based on SUPER_MAGIC present in
51  * the statfs(2) structure but which need extra code for each new filesystem added to
52  * the OS and for Linux that tends to be often as it has quite some different filesystems.
53  * This new implementation should eliminate this as we use the Linux /proc/mounts in kernel
54  * data which automatically adds any new filesystem when added to the kernel.
55  */
56
57 /*
58  *  Marco van Wieringen, August 2009
59  */
60
61 #include "bacula.h"
62 #include "mntent_cache.h"
63
64 #include <errno.h>
65 #include <stdlib.h>
66 #include <string.h>
67 #include <sys/types.h>
68 #include <sys/stat.h>
69
70 #if defined(HAVE_GETMNTENT)
71 #if defined(HAVE_LINUX_OS) || defined(HAVE_HPUX_OS)
72 #include <mntent.h>
73 #elif defined(HAVE_SUN_OS)
74 #include <sys/mnttab.h>
75 #endif /* HAVE_GETMNTENT */
76 #elif defined(HAVE_GETMNTINFO)
77 #if defined(HAVE_OPENBSD_OS)
78 #include <sys/param.h>
79 #include <sys/mount.h>
80 #elif defined(HAVE_NETBSD_OS)
81 #include <sys/types.h>
82 #include <sys/statvfs.h>
83 #else
84 #include <sys/param.h>
85 #include <sys/ucred.h>
86 #include <sys/mount.h>
87 #endif
88 #elif defined(HAVE_AIX_OS)
89 #include <fshelp.h>
90 #include <sys/vfs.h>
91 #elif defined(HAVE_OSF1_OS)
92 #include <sys/mount.h>
93 #endif
94
95 static char cache_initialized = 0;
96
97 /**
98  * Protected data by mutex lock.
99  */
100 static pthread_mutex_t mntent_cache_lock = PTHREAD_MUTEX_INITIALIZER;
101 static mntent_cache_entry_t *mntent_cache_entry_hashtable[NR_MNTENT_CACHE_ENTRIES];
102 static mntent_cache_entry_t *previous_cache_hit = NULL;
103
104 /**
105  * Simple hash function.
106  */
107 static uint32_t mntent_hash_function(uint32_t dev)
108 {
109    return (dev % NR_MNTENT_CACHE_ENTRIES);
110 }
111
112 /**
113  * Add a new entry to the cache.
114  * This function should be called with a write lock on the mntent_cache.
115  */
116 static void add_mntent_mapping(uint32_t dev, const char *special, const char *mountpoint,
117                                const char *fstype, const char *mntopts)
118 {
119    uint32_t hash;
120    mntent_cache_entry_t *mce;
121
122    /**
123     * Select the correct hash bucket.
124     */
125    hash = mntent_hash_function(dev);
126
127    /**
128     * See if this is the first being put into the hash bucket.
129     */
130    if (mntent_cache_entry_hashtable[hash] == (mntent_cache_entry_t *)NULL) {
131       mce = (mntent_cache_entry_t *)malloc(sizeof(mntent_cache_entry_t));
132       memset((caddr_t)mce, 0, sizeof(mntent_cache_entry_t));
133       mntent_cache_entry_hashtable[hash] = mce;
134    } else {
135       /**
136        * Walk the linked list in the hash bucket.
137        */
138       for (mce = mntent_cache_entry_hashtable[hash]; mce->next != NULL; mce = mce->next) ;
139       mce->next = (mntent_cache_entry_t *)malloc(sizeof(mntent_cache_entry_t));
140       mce = mce->next;
141       memset((caddr_t)mce, 0, sizeof(mntent_cache_entry_t));
142    }
143
144    mce->dev = dev;
145    mce->special = bstrdup(special);
146    mce->mountpoint = bstrdup(mountpoint);
147    mce->fstype = bstrdup(fstype);
148    if (mntopts) {
149       mce->mntopts = bstrdup(mntopts);
150    }
151 }
152
153 /**
154  * OS specific function to load the different mntents into the cache.
155  * This function should be called with a write lock on the mntent_cache.
156  */
157 static void refresh_mount_cache(void)
158 {
159 #if defined(HAVE_GETMNTENT)
160    FILE *fp;
161    struct stat st;
162 #if defined(HAVE_LINUX_OS) || defined(HAVE_HPUX_OS) || defined(HAVE_IRIX_OS)
163    struct mntent *mnt;
164
165 #if defined(HAVE_LINUX_OS)
166    if ((fp = setmntent("/proc/mounts", "r")) == (FILE *)NULL) {
167       if ((fp = setmntent(_PATH_MOUNTED, "r")) == (FILE *)NULL) {
168          return;
169       }
170    }
171 #elif defined(HAVE_HPUX_OS)
172    if ((fp = fopen(MNT_MNTTAB, "r")) == (FILE *)NULL) {
173       return;
174    }
175 #elif defined(HAVE_IRIX_OS)
176    if ((fp = setmntent(MOUNTED, "r")) == (FILE *)NULL) {
177       return;
178    }
179 #endif
180
181    while ((mnt = getmntent(fp)) != (struct mntent *)NULL) {
182       if (stat(mnt->mnt_dir, &st) < 0) {
183          continue;
184       }
185
186       add_mntent_mapping(st.st_dev, mnt->mnt_fsname, mnt->mnt_dir, mnt->mnt_type, mnt->mnt_opts);
187    }
188
189    endmntent(fp);
190 #elif defined(HAVE_SUN_OS)
191    struct mnttab mnt;
192
193    if ((fp = fopen(MNTTAB, "r")) == (FILE *)NULL)
194       return;
195
196    while (getmntent(fp, &mnt) == 0) {
197       if (stat(mnt.mnt_mountp, &st) < 0) {
198          continue;
199       }
200
201       add_mntent_mapping(st.st_dev, mnt.mnt_special, mnt.mnt_mountp, mnt.mnt_fstype, mnt.mnt_mntopts);
202    }
203
204    fclose(fp);
205 #endif /* HAVE_SUN_OS */
206 #elif defined(HAVE_GETMNTINFO)
207    int cnt;
208    struct stat st;
209 #if defined(HAVE_NETBSD_OS)
210    struct statvfs *mntinfo;
211 #else
212    struct statfs *mntinfo;
213 #endif
214 #if defined(ST_NOWAIT)
215    int flags = ST_NOWAIT;
216 #elif defined(MNT_NOWAIT)
217    int flags = MNT_NOWAIT;
218 #else
219    int flags = 0;
220 #endif
221
222    if ((cnt = getmntinfo(&mntinfo, flags)) > 0) {
223       while (cnt > 0) {
224          if (stat(mntinfo->f_mntonname, &st) == 0) {
225             add_mntent_mapping(st.st_dev,
226                                mntinfo->f_mntfromname,
227                                mntinfo->f_mntonname,
228                                mntinfo->f_fstypename,
229                                NULL);
230          }
231          mntinfo++;
232          cnt--;
233       }
234    }
235 #elif defined(HAVE_AIX_OS)
236    int bufsize;
237    char *entries, *current;
238    struct vmount *vmp;
239    struct stat st;
240    struct vfs_ent *ve;
241    int n_entries, cnt;
242
243    if (mntctl(MCTL_QUERY, sizeof(bufsize), (struct vmount *)&bufsize) != 0) {
244       return;
245    }
246
247    entries = malloc(bufsize);
248    if ((n_entries = mntctl(MCTL_QUERY, bufsize, (struct vmount *) entries)) < 0) {
249       free(entries);
250       return;
251    }
252
253    cnt = 0;
254    current = entries;
255    while (cnt < n_entries) {
256       vmp = (struct vmount *)current;
257
258       if (stat(current + vmp->vmt_data[VMT_STUB].vmt_off, &st) < 0) {
259          continue;
260       }
261
262       ve = getvfsbytype(vmp->vmt_gfstype);
263       if (ve && ve->vfsent_name) {
264          add_mntent_mapping(st.st_dev,
265                             current + vmp->vmt_data[VMT_OBJECT].vmt_off,
266                             current + vmp->vmt_data[VMT_STUB].vmt_off,
267                             ve->vfsent_name,
268                             current + vmp->vmt_data[VMT_ARGS].vmt_off);
269       }
270       current = current + vmp->vmt_length;
271       cnt++;
272    }
273    free(entries);
274 #elif defined(HAVE_OSF1_OS)
275    struct statfs *entries, *current;
276    struct stat st;
277    int n_entries, cnt;
278    int size;
279
280    if ((n_entries = getfsstat((struct statfs *)0, 0L, MNT_NOWAIT)) < 0) {
281       return;
282    }
283
284    size = (n_entries + 1) * sizeof(struct statfs);
285    entries = malloc(size);
286
287    if ((n_entries = getfsstat(entries, size, MNT_NOWAIT)) < 0) {
288       free(entries);
289       return;
290    }
291
292    cnt = 0;
293    current = entries;
294    while (cnt < n_entries) {
295       if (stat(current->f_mntonname, &st) < 0) {
296          continue;
297       }
298       add_mntent_mapping(st.st_dev,
299                          current->f_mntfromname,
300                          current->f_mntonname,
301                          current->f_fstypename,
302                          NULL);
303       current++;
304       cnt++;
305    }
306    free(stats);
307 #endif
308 }
309
310 /**
311  * Clear the cache (either by flushing it or by initializing it.)
312  * This function should be called with a write lock on the mntent_cache.
313  */
314 static void clear_mount_cache()
315 {
316    uint32_t hash;
317    mntent_cache_entry_t *mce, *mce_next;
318
319    if (cache_initialized == 0) {
320       /**
321        * Initialize the hash table.
322        */
323       memset((caddr_t)mntent_cache_entry_hashtable, 0, NR_MNTENT_CACHE_ENTRIES * sizeof(mntent_cache_entry_t *));
324       cache_initialized = 1;
325    } else {
326       /**
327        * Clear the previous_cache_hit.
328        */
329       previous_cache_hit = NULL;
330
331       /**
332        * Walk all hash buckets.
333        */
334       for (hash = 0; hash < NR_MNTENT_CACHE_ENTRIES; hash++) {
335          /**
336           * Walk the content of this hash bucket.
337           */
338          mce = mntent_cache_entry_hashtable[hash];
339          mntent_cache_entry_hashtable[hash] = NULL;
340          while (mce != NULL) {
341             /**
342              * Save the pointer to the next entry.
343              */
344             mce_next = mce->next;
345
346             /**
347              * Free the structure.
348              */
349             if (mce->mntopts)
350                free(mce->mntopts);
351             free(mce->fstype);
352             free(mce->mountpoint);
353             free(mce->special);
354             free(mce);
355
356             mce = mce_next;
357          }
358       }
359    }
360 }
361
362 /**
363  * Initialize the cache for use.
364  */
365 static void initialize_mntent_cache(void)
366 {
367    /**
368     * Lock the cache while we update it.
369     */
370    P(mntent_cache_lock);
371
372    /**
373     * Make sure the cache is empty (either by flushing it or by initializing it.)
374     */
375    clear_mount_cache();
376
377    /**
378     * Refresh the cache.
379     */
380    refresh_mount_cache();
381
382    /**
383     * We are done updating the cache.
384     */
385    V(mntent_cache_lock);
386 }
387
388 void preload_mntent_cache(void)
389 {
390    initialize_mntent_cache();
391 }
392
393 void flush_mntent_cache(void)
394 {
395    /**
396     * Lock the cache while we update it.
397     */
398    P(mntent_cache_lock);
399
400    /**
401     * Make sure the cache is empty (either by flushing it or by initializing it.)
402     */
403    clear_mount_cache();
404
405    /**
406     * We are done updating the cache.
407     */
408    V(mntent_cache_lock);
409 }
410
411 /**
412  * Find a mapping in the cache.
413  */
414 mntent_cache_entry_t *find_mntent_mapping(uint32_t dev)
415 {
416    uint32_t hash;
417    mntent_cache_entry_t *mce;
418
419    /**
420     * Initialize the cache if that was not done before.
421     */
422    if (cache_initialized == 0) {
423       initialize_mntent_cache();
424    }
425
426    /**
427     * Shortcut when we get a request for the same device again.
428     */
429    if (previous_cache_hit && previous_cache_hit->dev == dev) {
430       return previous_cache_hit;
431    }
432
433    /**
434     * Lock the cache while we walk it.
435     */
436    P(mntent_cache_lock);
437
438    /**
439     * Select the correct hash bucket.
440     */
441    hash = mntent_hash_function(dev);
442
443    /**
444     * Walk the hash bucket.
445     */
446    for (mce = mntent_cache_entry_hashtable[hash]; mce != NULL; mce = mce->next) {
447       if (mce->dev == dev) {
448          previous_cache_hit = mce;
449          V(mntent_cache_lock);
450          return mce;
451       }
452    }
453
454    /**
455     * We are done walking the cache.
456     */
457    V(mntent_cache_lock);
458    return NULL;
459 }