]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/mntent_cache.c
Reverted the change of TRU64 to OSF1 for the OS detection rules. Although Compaq...
[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 (Tru64)
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) || defined(HAVE_AIX_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) || defined(HAVE_AIX_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 #elif defined(HAVE_AIX_OS)
180    if ((fp = setmntent(MNTTAB, "r")) == (FILE *)NULL) {
181       return;
182    }
183 #endif
184
185    while ((mnt = getmntent(fp)) != (struct mntent *)NULL) {
186       if (stat(mnt->mnt_dir, &st) < 0) {
187          continue;
188       }
189
190       add_mntent_mapping(st.st_dev, mnt->mnt_fsname, mnt->mnt_dir, mnt->mnt_type, mnt->mnt_opts);
191    }
192
193    endmntent(fp);
194 #elif defined(HAVE_SUN_OS)
195    struct mnttab mnt;
196
197    if ((fp = fopen(MNTTAB, "r")) == (FILE *)NULL)
198       return;
199
200    while (getmntent(fp, &mnt) == 0) {
201       if (stat(mnt.mnt_mountp, &st) < 0) {
202          continue;
203       }
204
205       add_mntent_mapping(st.st_dev, mnt.mnt_special, mnt.mnt_mountp, mnt.mnt_fstype, mnt.mnt_mntopts);
206    }
207
208    fclose(fp);
209 #endif /* HAVE_SUN_OS */
210 #elif defined(HAVE_GETMNTINFO)
211    int cnt;
212    struct stat st;
213 #if defined(HAVE_NETBSD_OS)
214    struct statvfs *mntinfo;
215 #else
216    struct statfs *mntinfo;
217 #endif
218 #if defined(ST_NOWAIT)
219    int flags = ST_NOWAIT;
220 #elif defined(MNT_NOWAIT)
221    int flags = MNT_NOWAIT;
222 #else
223    int flags = 0;
224 #endif
225
226    if ((cnt = getmntinfo(&mntinfo, flags)) > 0) {
227       while (cnt > 0) {
228          if (stat(mntinfo->f_mntonname, &st) == 0) {
229             add_mntent_mapping(st.st_dev,
230                                mntinfo->f_mntfromname,
231                                mntinfo->f_mntonname,
232                                mntinfo->f_fstypename,
233                                NULL);
234          }
235          mntinfo++;
236          cnt--;
237       }
238    }
239 #elif defined(HAVE_AIX_OS)
240    int bufsize;
241    char *entries, *current;
242    struct vmount *vmp;
243    struct stat st;
244    struct vfs_ent *ve;
245    int n_entries, cnt;
246
247    if (mntctl(MCTL_QUERY, sizeof(bufsize), (struct vmount *)&bufsize) != 0) {
248       return;
249    }
250
251    entries = malloc(bufsize);
252    if ((n_entries = mntctl(MCTL_QUERY, bufsize, (struct vmount *) entries)) < 0) {
253       free(entries);
254       return;
255    }
256
257    cnt = 0;
258    current = entries;
259    while (cnt < n_entries) {
260       vmp = (struct vmount *)current;
261
262       if (stat(current + vmp->vmt_data[VMT_STUB].vmt_off, &st) < 0) {
263          continue;
264       }
265
266       ve = getvfsbytype(vmp->vmt_gfstype);
267       if (ve && ve->vfsent_name) {
268          add_mntent_mapping(st.st_dev,
269                             current + vmp->vmt_data[VMT_OBJECT].vmt_off,
270                             current + vmp->vmt_data[VMT_STUB].vmt_off,
271                             ve->vfsent_name,
272                             current + vmp->vmt_data[VMT_ARGS].vmt_off);
273       }
274       current = current + vmp->vmt_length;
275       cnt++;
276    }
277    free(entries);
278 #elif defined(HAVE_OSF1_OS)
279    struct statfs *entries, *current;
280    struct stat st;
281    int n_entries, cnt;
282    int size;
283
284    if ((n_entries = getfsstat((struct statfs *)0, 0L, MNT_NOWAIT)) < 0) {
285       return;
286    }
287
288    size = (n_entries + 1) * sizeof(struct statfs);
289    entries = malloc(size);
290
291    if ((n_entries = getfsstat(entries, size, MNT_NOWAIT)) < 0) {
292       free(entries);
293       return;
294    }
295
296    cnt = 0;
297    current = entries;
298    while (cnt < n_entries) {
299       if (stat(current->f_mntonname, &st) < 0) {
300          continue;
301       }
302       add_mntent_mapping(st.st_dev,
303                          current->f_mntfromname,
304                          current->f_mntonname,
305                          current->f_fstypename,
306                          NULL);
307       current++;
308       cnt++;
309    }
310    free(stats);
311 #endif
312 }
313
314 /**
315  * Clear the cache (either by flushing it or by initializing it.)
316  * This function should be called with a write lock on the mntent_cache.
317  */
318 static void clear_mount_cache()
319 {
320    uint32_t hash;
321    mntent_cache_entry_t *mce, *mce_next;
322
323    if (cache_initialized == 0) {
324       /**
325        * Initialize the hash table.
326        */
327       memset((caddr_t)mntent_cache_entry_hashtable, 0, NR_MNTENT_CACHE_ENTRIES * sizeof(mntent_cache_entry_t *));
328       cache_initialized = 1;
329    } else {
330       /**
331        * Clear the previous_cache_hit.
332        */
333       previous_cache_hit = NULL;
334
335       /**
336        * Walk all hash buckets.
337        */
338       for (hash = 0; hash < NR_MNTENT_CACHE_ENTRIES; hash++) {
339          /**
340           * Walk the content of this hash bucket.
341           */
342          mce = mntent_cache_entry_hashtable[hash];
343          mntent_cache_entry_hashtable[hash] = NULL;
344          while (mce != NULL) {
345             /**
346              * Save the pointer to the next entry.
347              */
348             mce_next = mce->next;
349
350             /**
351              * Free the structure.
352              */
353             if (mce->mntopts)
354                free(mce->mntopts);
355             free(mce->fstype);
356             free(mce->mountpoint);
357             free(mce->special);
358             free(mce);
359
360             mce = mce_next;
361          }
362       }
363    }
364 }
365
366 /**
367  * Initialize the cache for use.
368  */
369 static void initialize_mntent_cache(void)
370 {
371    /**
372     * Lock the cache while we update it.
373     */
374    P(mntent_cache_lock);
375
376    /**
377     * Make sure the cache is empty (either by flushing it or by initializing it.)
378     */
379    clear_mount_cache();
380
381    /**
382     * Refresh the cache.
383     */
384    refresh_mount_cache();
385
386    /**
387     * We are done updating the cache.
388     */
389    V(mntent_cache_lock);
390 }
391
392 void preload_mntent_cache(void)
393 {
394    initialize_mntent_cache();
395 }
396
397 void flush_mntent_cache(void)
398 {
399    /**
400     * Lock the cache while we update it.
401     */
402    P(mntent_cache_lock);
403
404    /**
405     * Make sure the cache is empty (either by flushing it or by initializing it.)
406     */
407    clear_mount_cache();
408
409    /**
410     * We are done updating the cache.
411     */
412    V(mntent_cache_lock);
413 }
414
415 /**
416  * Find a mapping in the cache.
417  */
418 mntent_cache_entry_t *find_mntent_mapping(uint32_t dev)
419 {
420    uint32_t hash;
421    mntent_cache_entry_t *mce;
422
423    /**
424     * Initialize the cache if that was not done before.
425     */
426    if (cache_initialized == 0) {
427       initialize_mntent_cache();
428    }
429
430    /**
431     * Shortcut when we get a request for the same device again.
432     */
433    if (previous_cache_hit && previous_cache_hit->dev == dev) {
434       return previous_cache_hit;
435    }
436
437    /**
438     * Lock the cache while we walk it.
439     */
440    P(mntent_cache_lock);
441
442    /**
443     * Select the correct hash bucket.
444     */
445    hash = mntent_hash_function(dev);
446
447    /**
448     * Walk the hash bucket.
449     */
450    for (mce = mntent_cache_entry_hashtable[hash]; mce != NULL; mce = mce->next) {
451       if (mce->dev == dev) {
452          previous_cache_hit = mce;
453          V(mntent_cache_lock);
454          return mce;
455       }
456    }
457
458    /**
459     * We are done walking the cache.
460     */
461    V(mntent_cache_lock);
462    return NULL;
463 }