Annotation of fam/fam/FileSystemTable.c++, Revision 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>