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