Annotation of fam/fam/FileSystemTable.c++, Revision 1.1.1.1
1.1 trev 1: // Copyright (C) 1999 Silicon Graphics, Inc. All Rights Reserved.
2: //
3: // This program is free software; you can redistribute it and/or modify it
4: // under the terms of version 2 of the GNU General Public License as
5: // published by the Free Software Foundation.
6: //
7: // This program is distributed in the hope that it would be useful, but
8: // WITHOUT ANY WARRANTY; without even the implied warranty of
9: // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Further, any
10: // license provided herein, whether implied or otherwise, is limited to
11: // this program in accordance with the express provisions of the GNU
12: // General Public License. Patent licenses, if any, provided herein do not
13: // apply to combinations of this program with other product or programs, or
14: // any other product whatsoever. This program is distributed without any
15: // warranty that the program is delivered free of the rightful claim of any
16: // third person by way of infringement or the like. See the GNU General
17: // Public License for more details.
18: //
19: // You should have received a copy of the GNU General Public License along
20: // with this program; if not, write the Free Software Foundation, Inc., 59
21: // Temple Place - Suite 330, Boston MA 02111-1307, USA.
22:
23: #include <stddef.h>
24: #include "FileSystemTable.h"
25:
26: #include <mntent.h>
27: #include <stdlib.h>
28: #include <string.h>
29:
30: #if HAVE_STATVFS
31: #include <sys/statvfs.h>
32: #endif
33:
34: #include "Cred.h"
35: #include "Event.h"
36: #include "FileSystem.h"
37: #include "InternalClient.h"
38: #include "LocalFileSystem.h"
39: #include "Log.h"
40: #include "NFSFileSystem.h"
41:
42: // Fam has two tables of mounted filesystems -- fs_by_name and
43: // fs_by_id. They are keyed by mountpoint and by filesystem ID,
44: // respectively. fs_by_id is lazily filled in as needed (so we only
45: // do as many statvfs calls as needed -- fam may hang if the NFS
46: // server is down). fs_by_name is completely rebuilt when /etc/mtab
47: // is changed, and fs_by_id is destroyed, to be lazily re-filled
48: // later.
49:
50: // Class Variables
51:
52: unsigned int FileSystemTable::count;
53: FileSystemTable::IDTable FileSystemTable::fs_by_id;
54: FileSystemTable::NameTable *FileSystemTable::fs_by_name;
55: const char FileSystemTable::mtab_name[] = MOUNTED;
56: InternalClient *FileSystemTable::mtab_watcher;
57: FileSystem *FileSystemTable::root;
58:
59: #ifdef HAPPY_PURIFY
60:
61: //////////////////////////////////////////////////////////////////////////////
62: // The constructor and destructor simply maintain a refcount of the
63: // files that include FileSystemTable.h. When the last reference
64: // is destroyed, the mtab watcher is turned off, and the fs_by_name
65: // table is destroyed.
66:
67: FileSystemTable::FileSystemTable()
68: {
69: count++;
70: }
71:
72: FileSystemTable::~FileSystemTable()
73: {
74: if (!--count)
75: { delete mtab_watcher;
76: mtab_watcher = NULL;
77: if (fs_by_name)
78: { destroy_fses(fs_by_name);
79: delete fs_by_name;
80: fs_by_name = NULL;
81: }
82: }
83: }
84:
85: #endif /* HAPPY_PURIFY */
86:
87: //////////////////////////////////////////////////////////////////////////////
88: // fs_by_name is a table that maps mount directories to FileSystem pointers.
89: //
90: // It is built the first time FileSystemTable::find() is called, and
91: // it's rebuilt whenever /etc/mtab changes. When it is rebuilt,
92: // existing FileSystems are moved to the new table. This is done
93: // because each ClientInterest has a pointer to its FileSystem,
94: // and we don't want to change all ClientInterests, nor do we want
95: // two FileSystem structures representing the same file system.
96:
97:
98: void
99: FileSystemTable::create_fs_by_name()
100: {
101: NameTable *new_fs_by_name = new NameTable;
102: NameTable mount_parents, dismounted_fses;
103:
104: if (fs_by_name)
105: dismounted_fses = *fs_by_name;
106:
107: // Read /etc/mtab.
108: Cred::SuperUser.become_user();
109: FILE *mtab = setmntent(mtab_name, "r");
110: if(mtab == NULL)
111: {
112: Log::error("couldn't open %s for reading", mtab_name);
113: delete new_fs_by_name;
114: return;
115: }
116: root = NULL;
117: for (mntent *mp; ((mp = getmntent(mtab)) != NULL); )
118: {
119: FileSystem *fs = fs_by_name ? fs_by_name->find(mp->mnt_dir) : NULL;
120: if (fs && fs->matches(*mp))
121: {
122: Log::debug("mtab: MATCH \"%s\" on \"%s\" using type <%s>",
123: mp->mnt_fsname, mp->mnt_dir, mp->mnt_type);
124:
125: new_fs_by_name->insert(mp->mnt_dir, fs);
126: if (dismounted_fses.find(mp->mnt_dir))
127: dismounted_fses.remove(mp->mnt_dir);
128: }
129: else
130: {
131:
132: if ((!strcmp(mp->mnt_type, MNTTYPE_NFS)
133: #if HAVE_MNTTYPE_NFS2
134: || !strcmp(mp->mnt_type, MNTTYPE_NFS2)
135: #endif
136: #if HAVE_MNTTYPE_NFS3
137: || !strcmp(mp->mnt_type, MNTTYPE_NFS3)
138: #endif
139: #if HAVE_MNTTYPE_CACHEFS
140: || !strcmp(mp->mnt_type, MNTTYPE_CACHEFS)
141: #endif
142: ) && strchr(mp->mnt_fsname, ':'))
143: {
144: if(Log::get_level() == Log::DEBUG)
145: {
146: const char *mntopt = hasmntopt(mp, "dev");
147: if(mntopt == NULL) mntopt = "";
148: Log::debug("mtab: new NFS \"%s\" on \"%s\" %s using <%s>",
149: mp->mnt_fsname, mp->mnt_dir, mntopt,
150: mp->mnt_type);
151: }
152:
153: fs = new NFSFileSystem(*mp);
154: }
155: else
156: {
157: Log::debug("mtab: new local \"%s\" on \"%s\"",
158: mp->mnt_fsname, mp->mnt_dir);
159:
160: fs = new LocalFileSystem(*mp);
161: }
162: new_fs_by_name->insert(mp->mnt_dir, fs);
163: if (fs_by_name)
164: {
165: // Find parent filesystem.
166:
167: FileSystem *parent = longest_prefix(mp->mnt_dir);
168: assert(parent);
169: mount_parents.insert(parent->dir(), parent);
170: }
171: }
172: if (!strcmp(mp->mnt_dir, "/"))
173: root = fs;
174: }
175: endmntent(mtab);
176:
177: if(root == NULL)
178: {
179: assert(root);
180: Log::error("couldn't find / in %s", mtab_name);
181: delete new_fs_by_name;
182: return; // horrible... we're not in a good state, the
183: // now-brain-damaged fs_by_name is hanging around, etc.
184: // It might be better to exit.
185: }
186:
187: // Install the new table.
188:
189: delete fs_by_name;
190: fs_by_name = new_fs_by_name;
191:
192: // Relocate all interests in parents of new filesystems.
193: // We relocate interests out of parents before relocating
194: // out of dismounted filesystems in the hope that we can
195: // avoid relocating some interests twice. Consider the case
196: // where /mnt/foo is an interest, and we simultaneously
197: // learn that /mnt was dismounted and /fred was mounted.
198: // We don't want to relocate /mnt/foo to /, then test
199: // it for relocation to /fred.
200:
201: unsigned i;
202: FileSystem *fs;
203: for (i = 0; ((fs = mount_parents.value(i)) != NULL); i++)
204: {
205: Log::debug("mtab: relocating in parent \"%s\"", fs->dir());
206: fs->relocate_interests();
207: }
208:
209: // Relocate all interests in dismounted filesystems and destroy
210: // the filesystems.
211:
212: for (i = 0; ((fs = dismounted_fses.value(i)) != NULL); i++)
213: {
214: Log::debug("mtab: dismount \"%s\" on \"%s\"",
215: fs->fsname(), fs->dir());
216:
217: fs->relocate_interests();
218: delete fs;
219: }
220: Log::debug("mtab done.");
221: }
222:
223: void
224: FileSystemTable::destroy_fses(NameTable *fstab)
225: {
226: // Destroy any unreclaimed filesystems.
227:
228: for (unsigned i = 0; fstab->key(i); i++)
229: delete fstab->value(i);
230: }
231:
232: void
233: FileSystemTable::mtab_event_handler(const Event& event, void *)
234: {
235: if (event == Event::Changed)
236: {
237: Log::debug("%s changed, rebuilding filesystem table", mtab_name);
238: fs_by_id.removeAll();
239: create_fs_by_name();
240: }
241: }
242:
243: //////////////////////////////////////////////////////////////////////////////
244: //
245:
246: FileSystem *
247: FileSystemTable::find(const char *path, const Cred& cr)
248: {
249: char temp_path[PATH_MAX];
250: FileSystem *fs = NULL;
251:
252: assert(path[0] == '/');
253:
254: // (Initialize fs_by_name if necessary.) As a side effect,
255: // create_fs_by_name initializes our "root" member variable.
256: if (!fs_by_name)
257: { create_fs_by_name();
258: mtab_watcher = new InternalClient(mtab_name, mtab_event_handler, NULL);
259: }
260:
261: cr.become_user();
262:
263: // If !HAVE_STATVFS, we could use statfs instead, but the statfs.f_fsid
264: // is not set reliably on Linux, so it's useless. We'll do every lookup
265: // by name; hopefully that doesn't suck.
266: #if HAVE_STATVFS
267:
268: // Perform a statvfs(2) on the first existing ancestor.
269:
270: struct statvfs fs_status;
271: int rc = statvfs(path, &fs_status);
272: if (rc < 0)
273: { (void) strcpy(temp_path, path);
274: while (rc < 0)
275: { char *slash = strrchr(temp_path, '/');
276: if (!slash)
277: return root;
278: *slash = '\0';
279: rc = statvfs(temp_path, &fs_status);
280: }
281: }
282:
283: // Look up filesystem by ID.
284:
285: fs = fs_by_id.find(fs_status.f_fsid);
286: if (fs)
287: return fs;
288: #endif
289:
290: // Convert to real path. Look up real path in fs_by_name().
291:
292: cr.become_user();
293: (void) realpath(path, temp_path);
294: fs = longest_prefix(temp_path);
295:
296: // Insert into fs_by_id.
297:
298: #if HAVE_STATVFS
299: fs_by_id.insert(fs_status.f_fsid, fs);
300: #endif
301:
302: return fs;
303: }
304:
305: FileSystem *
306: FileSystemTable::longest_prefix(const char *path)
307: {
308: FileSystem * bestmatch = root;
309: int maxmatch = -1;
310: const char *key;
311: for (unsigned i = 0; ((key = fs_by_name->key(i)) != NULL); i++)
312: { for (int j = 0; ; j++)
313: { if (!key[j])
314: { if ((!path[j] || path[j] == '/') && j > maxmatch)
315: { maxmatch = j;
316: bestmatch = fs_by_name->value(i);
317: }
318: break;
319: }
320: if (key[j] != path[j])
321: break;
322: }
323: }
324: assert(bestmatch);
325: return bestmatch;
326: }
327:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>