]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/mntent_cache.c
Switch from GPLv2 to AGPLv3
[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_DARWIN_OS)
78 #include <sys/param.h>
79 #include <sys/ucred.h> 
80 #include <sys/mount.h> 
81 #else
82 #include <sys/types.h>
83 #include <sys/statvfs.h>
84 #endif
85 #elif defined(HAVE_AIX_OS)
86 #include <fshelp.h>
87 #include <sys/vfs.h>
88 #elif defined(HAVE_OSF1_OS)
89 #include <sys/mount.h>
90 #endif
91
92 static char cache_initialized = 0;
93
94 /**
95  * Protected data by mutex lock.
96  */
97 static pthread_mutex_t mntent_cache_lock = PTHREAD_MUTEX_INITIALIZER;
98 static mntent_cache_entry_t *mntent_cache_entry_hashtable[NR_MNTENT_CACHE_ENTRIES];
99 static mntent_cache_entry_t *previous_cache_hit = NULL;
100
101 /**
102  * Simple hash function.
103  */
104 static uint32_t mntent_hash_function(uint32_t dev)
105 {
106    return (dev % NR_MNTENT_CACHE_ENTRIES);
107 }
108
109 /**
110  * Add a new entry to the cache.
111  * This function should be called with a write lock on the mntent_cache.
112  */
113 static void add_mntent_mapping(uint32_t dev, const char *special, const char *mountpoint,
114                                const char *fstype, const char *mntopts)
115 {
116    uint32_t hash;
117    mntent_cache_entry_t *mce;
118
119    /**
120     * Select the correct hash bucket.
121     */
122    hash = mntent_hash_function(dev);
123
124    /**
125     * See if this is the first being put into the hash bucket.
126     */
127    if (mntent_cache_entry_hashtable[hash] == (mntent_cache_entry_t *)NULL) {
128       mce = (mntent_cache_entry_t *)malloc(sizeof(mntent_cache_entry_t));
129       memset((caddr_t)mce, 0, sizeof(mntent_cache_entry_t));
130       mntent_cache_entry_hashtable[hash] = mce;
131    } else {
132       /**
133        * Walk the linked list in the hash bucket.
134        */
135       for (mce = mntent_cache_entry_hashtable[hash]; mce->next != NULL; mce = mce->next) ;
136       mce->next = (mntent_cache_entry_t *)malloc(sizeof(mntent_cache_entry_t));
137       mce = mce->next;
138       memset((caddr_t)mce, 0, sizeof(mntent_cache_entry_t));
139    }
140
141    mce->dev = dev;
142    mce->special = bstrdup(special);
143    mce->mountpoint = bstrdup(mountpoint);
144    mce->fstype = bstrdup(fstype);
145    if (mntopts) {
146       mce->mntopts = bstrdup(mntopts);
147    }
148 }
149
150 /**
151  * OS specific function to load the different mntents into the cache.
152  * This function should be called with a write lock on the mntent_cache.
153  */
154 static void refresh_mount_cache(void)
155 {
156 #if defined(HAVE_GETMNTENT)
157    FILE *fp;
158    struct stat st;
159 #if defined(HAVE_LINUX_OS) || defined(HAVE_HPUX_OS) || defined(HAVE_IRIX_OS)
160    struct mntent *mnt;
161
162 #if defined(HAVE_LINUX_OS)
163    if ((fp = setmntent("/proc/mounts", "r")) == (FILE *)NULL) {
164       if ((fp = setmntent(_PATH_MOUNTED, "r")) == (FILE *)NULL) {
165          return;
166       }
167    }
168 #elif defined(HAVE_HPUX_OS)
169    if ((fp = fopen(MNT_MNTTAB, "r")) == (FILE *)NULL) {
170       return;
171    }
172 #elif defined(HAVE_IRIX_OS)
173    if ((fp = setmntent(MOUNTED, "r")) == (FILE *)NULL) {
174       return;
175    }
176 #endif
177
178    while ((mnt = getmntent(fp)) != (struct mntent *)NULL) {
179       if (stat(mnt->mnt_dir, &st) < 0) {
180          continue;
181       }
182
183       add_mntent_mapping(st.st_dev, mnt->mnt_fsname, mnt->mnt_dir, mnt->mnt_type, mnt->mnt_opts);
184    }
185
186    endmntent(fp);
187 #elif defined(HAVE_SUN_OS)
188    struct mnttab mnt;
189
190    if ((fp = fopen(MNTTAB, "r")) == (FILE *)NULL)
191       return;
192
193    while (getmntent(fp, &mnt) == 0) {
194       if (stat(mnt.mnt_mountp, &st) < 0) {
195          continue;
196       }
197
198       add_mntent_mapping(st.st_dev, mnt.mnt_special, mnt.mnt_mountp, mnt.mnt_fstype, mnt.mnt_mntopts);
199    }
200
201    fclose(fp);
202 #endif /* HAVE_SUN_OS */
203 #elif defined(HAVE_GETMNTINFO)
204    int cnt;
205    struct stat st;
206 #if defined(HAVE_DARWIN_OS)
207    struct statfs *mntinfo;
208 #else
209    struct statvfs *mntinfo;
210 #endif
211
212    if ((cnt = getmntinfo(&mntinfo, MNT_NOWAIT)) > 0) {
213       while (cnt > 0) {
214          if (stat(mntinfo->f_mntonname, &st) == 0) {
215             add_mntent_mapping(st.st_dev,
216                                mntinfo->f_mntfromname,
217                                mntinfo->f_mntonname,
218                                mntinfo->f_fstypename,
219                                NULL);
220          }
221          mntinfo++;
222          cnt--;
223       }
224    }
225 #elif defined(HAVE_AIX_OS)
226    int bufsize;
227    char *entries, *current;
228    struct vmount *vmp;
229    struct stat st;
230    struct vfs_ent *ve;
231    int n_entries, cnt;
232
233    if (mntctl(MCTL_QUERY, sizeof(bufsize), (struct vmount *)&bufsize) != 0) {
234       return;
235    }
236
237    entries = malloc(bufsize);
238    if ((n_entries = mntctl(MCTL_QUERY, bufsize, (struct vmount *) entries)) < 0) {
239       free(entries);
240       return;
241    }
242
243    cnt = 0;
244    current = entries;
245    while (cnt < n_entries) {
246       vmp = (struct vmount *)current;
247
248       if (stat(current + vmp->vmt_data[VMT_STUB].vmt_off, &st) < 0) {
249          continue;
250       }
251
252       ve = getvfsbytype(vmp->vmt_gfstype);
253       if (ve && ve->vfsent_name) {
254          add_mntent_mapping(st.st_dev,
255                             current + vmp->vmt_data[VMT_OBJECT].vmt_off,
256                             current + vmp->vmt_data[VMT_STUB].vmt_off,
257                             ve->vfsent_name,
258                             current + vmp->vmt_data[VMT_ARGS].vmt_off);
259       }
260       current = current + vmp->vmt_length;
261       cnt++;
262    }
263    free(entries);
264 #elif defined(HAVE_OSF1_OS)
265    struct statfs *entries, *current;
266    struct stat st;
267    int n_entries, cnt;
268    int size;
269
270    if ((n_entries = getfsstat((struct statfs *)0, 0L, MNT_NOWAIT)) < 0) {
271       return;
272    }
273
274    size = (n_entries + 1) * sizeof(struct statfs);
275    entries = malloc(size);
276
277    if ((n_entries = getfsstat(entries, size, MNT_NOWAIT)) < 0) {
278       free(entries);
279       return;
280    }
281
282    cnt = 0;
283    current = entries;
284    while (cnt < n_entries) {
285       if (stat(current->f_mntonname, &st) < 0) {
286          continue;
287       }
288       add_mntent_mapping(st.st_dev,
289                          current->f_mntfromname,
290                          current->f_mntonname,
291                          current->f_fstypename,
292                          NULL);
293       current++;
294       cnt++;
295    }
296    free(stats);
297 #endif
298 }
299
300 /**
301  * Clear the cache (either by flushing it or by initializing it.)
302  * This function should be called with a write lock on the mntent_cache.
303  */
304 static void clear_mount_cache()
305 {
306    uint32_t hash;
307    mntent_cache_entry_t *mce, *mce_next;
308
309    if (cache_initialized == 0) {
310       /**
311        * Initialize the hash table.
312        */
313       memset((caddr_t)mntent_cache_entry_hashtable, 0, NR_MNTENT_CACHE_ENTRIES * sizeof(mntent_cache_entry_t *));
314       cache_initialized = 1;
315    } else {
316       /**
317        * Clear the previous_cache_hit.
318        */
319       previous_cache_hit = NULL;
320
321       /**
322        * Walk all hash buckets.
323        */
324       for (hash = 0; hash < NR_MNTENT_CACHE_ENTRIES; hash++) {
325          /**
326           * Walk the content of this hash bucket.
327           */
328          mce = mntent_cache_entry_hashtable[hash];
329          mntent_cache_entry_hashtable[hash] = NULL;
330          while (mce != NULL) {
331             /**
332              * Save the pointer to the next entry.
333              */
334             mce_next = mce->next;
335
336             /**
337              * Free the structure.
338              */
339             if (mce->mntopts)
340                free(mce->mntopts);
341             free(mce->fstype);
342             free(mce->mountpoint);
343             free(mce->special);
344             free(mce);
345
346             mce = mce_next;
347          }
348       }
349    }
350 }
351
352 /**
353  * Initialize the cache for use.
354  */
355 static void initialize_mntent_cache(void)
356 {
357    /**
358     * Lock the cache while we update it.
359     */
360    P(mntent_cache_lock);
361
362    /**
363     * Make sure the cache is empty (either by flushing it or by initializing it.)
364     */
365    clear_mount_cache();
366
367    /**
368     * Refresh the cache.
369     */
370    refresh_mount_cache();
371
372    /**
373     * We are done updating the cache.
374     */
375    V(mntent_cache_lock);
376 }
377
378 void preload_mntent_cache(void)
379 {
380    initialize_mntent_cache();
381 }
382
383 void flush_mntent_cache(void)
384 {
385    /**
386     * Lock the cache while we update it.
387     */
388    P(mntent_cache_lock);
389
390    /**
391     * Make sure the cache is empty (either by flushing it or by initializing it.)
392     */
393    clear_mount_cache();
394
395    /**
396     * We are done updating the cache.
397     */
398    V(mntent_cache_lock);
399 }
400
401 /**
402  * Find a mapping in the cache.
403  */
404 mntent_cache_entry_t *find_mntent_mapping(uint32_t dev)
405 {
406    uint32_t hash;
407    mntent_cache_entry_t *mce;
408
409    /**
410     * Initialize the cache if that was not done before.
411     */
412    if (cache_initialized == 0) {
413       initialize_mntent_cache();
414    }
415
416    /**
417     * Shortcut when we get a request for the same device again.
418     */
419    if (previous_cache_hit && previous_cache_hit->dev == dev) {
420       return previous_cache_hit;
421    }
422
423    /**
424     * Lock the cache while we walk it.
425     */
426    P(mntent_cache_lock);
427
428    /**
429     * Select the correct hash bucket.
430     */
431    hash = mntent_hash_function(dev);
432
433    /**
434     * Walk the hash bucket.
435     */
436    for (mce = mntent_cache_entry_hashtable[hash]; mce != NULL; mce = mce->next) {
437       if (mce->dev == dev) {
438          previous_cache_hit = mce;
439          V(mntent_cache_lock);
440          return mce;
441       }
442    }
443
444    /**
445     * We are done walking the cache.
446     */
447    V(mntent_cache_lock);
448    return NULL;
449 }