📄 filesystemtable.c++
字号:
// Copyright (C) 1999 Silicon Graphics, Inc. All Rights Reserved.// // This program is free software; you can redistribute it and/or modify it// under the terms of version 2 of the GNU General Public License as// published by the Free Software Foundation.//// This program is distributed in the hope that it would be useful, but// WITHOUT ANY WARRANTY; without even the implied warranty of// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Further, any// license provided herein, whether implied or otherwise, is limited to// this program in accordance with the express provisions of the GNU// General Public License. Patent licenses, if any, provided herein do not// apply to combinations of this program with other product or programs, or// any other product whatsoever. This program is distributed without any// warranty that the program is delivered free of the rightful claim of any// third person by way of infringement or the like. See the GNU General// Public License for more details.//// You should have received a copy of the GNU General Public License along// with this program; if not, write the Free Software Foundation, Inc., 59// Temple Place - Suite 330, Boston MA 02111-1307, USA.#include <stddef.h>#include "FileSystemTable.h"#include <mntent.h>#include <stdlib.h>#include <string.h>#if HAVE_STATVFS#include <sys/statvfs.h>#endif#include "Cred.h"#include "Event.h"#include "FileSystem.h"#include "InternalClient.h"#include "LocalFileSystem.h"#include "Log.h"#include "NFSFileSystem.h"// Fam has two tables of mounted filesystems -- fs_by_name and// fs_by_id. They are keyed by mountpoint and by filesystem ID,// respectively. fs_by_id is lazily filled in as needed (so we only// do as many statvfs calls as needed -- fam may hang if the NFS// server is down). fs_by_name is completely rebuilt when /etc/mtab// is changed, and fs_by_id is destroyed, to be lazily re-filled// later.// Class Variablesunsigned int FileSystemTable::count;FileSystemTable::IDTable FileSystemTable::fs_by_id;FileSystemTable::NameTable *FileSystemTable::fs_by_name;const char FileSystemTable::mtab_name[] = MOUNTED;InternalClient *FileSystemTable::mtab_watcher;FileSystem *FileSystemTable::root;#ifdef HAPPY_PURIFY//////////////////////////////////////////////////////////////////////////////// The constructor and destructor simply maintain a refcount of the// files that include FileSystemTable.h. When the last reference// is destroyed, the mtab watcher is turned off, and the fs_by_name// table is destroyed.FileSystemTable::FileSystemTable(){ count++;}FileSystemTable::~FileSystemTable(){ if (!--count) { delete mtab_watcher; mtab_watcher = NULL; if (fs_by_name) { destroy_fses(fs_by_name); delete fs_by_name; fs_by_name = NULL; } }}#endif /* HAPPY_PURIFY *///////////////////////////////////////////////////////////////////////////////// fs_by_name is a table that maps mount directories to FileSystem pointers.//// It is built the first time FileSystemTable::find() is called, and// it's rebuilt whenever /etc/mtab changes. When it is rebuilt,// existing FileSystems are moved to the new table. This is done// because each ClientInterest has a pointer to its FileSystem,// and we don't want to change all ClientInterests, nor do we want// two FileSystem structures representing the same file system.voidFileSystemTable::create_fs_by_name(){ NameTable *new_fs_by_name = new NameTable; NameTable mount_parents, dismounted_fses; if (fs_by_name) dismounted_fses = *fs_by_name; // Read /etc/mtab. Cred::SuperUser.become_user(); FILE *mtab = setmntent(mtab_name, "r"); if(mtab == NULL) { Log::error("couldn't open %s for reading", mtab_name); delete new_fs_by_name; return; } root = NULL; for (mntent *mp; ((mp = getmntent(mtab)) != NULL); ) { FileSystem *fs = fs_by_name ? fs_by_name->find(mp->mnt_dir) : NULL; if (fs && fs->matches(*mp)) { Log::debug("mtab: MATCH \"%s\" on \"%s\" using type <%s>", mp->mnt_fsname, mp->mnt_dir, mp->mnt_type); new_fs_by_name->insert(mp->mnt_dir, fs); if (dismounted_fses.find(mp->mnt_dir)) dismounted_fses.remove(mp->mnt_dir); } else { if ((!strcmp(mp->mnt_type, MNTTYPE_NFS)#if HAVE_MNTTYPE_NFS2 || !strcmp(mp->mnt_type, MNTTYPE_NFS2)#endif#if HAVE_MNTTYPE_NFS3 || !strcmp(mp->mnt_type, MNTTYPE_NFS3)#endif#if HAVE_MNTTYPE_CACHEFS || !strcmp(mp->mnt_type, MNTTYPE_CACHEFS)#endif ) && strchr(mp->mnt_fsname, ':')) { if(Log::get_level() == Log::DEBUG) { const char *mntopt = hasmntopt(mp, "dev"); if(mntopt == NULL) mntopt = ""; Log::debug("mtab: new NFS \"%s\" on \"%s\" %s using <%s>", mp->mnt_fsname, mp->mnt_dir, mntopt, mp->mnt_type); } fs = new NFSFileSystem(*mp); } else { Log::debug("mtab: new local \"%s\" on \"%s\"", mp->mnt_fsname, mp->mnt_dir); fs = new LocalFileSystem(*mp); } new_fs_by_name->insert(mp->mnt_dir, fs); if (fs_by_name) { // Find parent filesystem. FileSystem *parent = longest_prefix(mp->mnt_dir); assert(parent); mount_parents.insert(parent->dir(), parent); } } if (!strcmp(mp->mnt_dir, "/")) root = fs; } endmntent(mtab); if(root == NULL) { assert(root); Log::error("couldn't find / in %s", mtab_name); delete new_fs_by_name; return; // horrible... we're not in a good state, the // now-brain-damaged fs_by_name is hanging around, etc. // It might be better to exit. } // Install the new table. delete fs_by_name; fs_by_name = new_fs_by_name; // Relocate all interests in parents of new filesystems. // We relocate interests out of parents before relocating // out of dismounted filesystems in the hope that we can // avoid relocating some interests twice. Consider the case // where /mnt/foo is an interest, and we simultaneously // learn that /mnt was dismounted and /fred was mounted. // We don't want to relocate /mnt/foo to /, then test // it for relocation to /fred. unsigned i; FileSystem *fs; for (i = 0; ((fs = mount_parents.value(i)) != NULL); i++) { Log::debug("mtab: relocating in parent \"%s\"", fs->dir()); fs->relocate_interests(); } // Relocate all interests in dismounted filesystems and destroy // the filesystems. for (i = 0; ((fs = dismounted_fses.value(i)) != NULL); i++) { Log::debug("mtab: dismount \"%s\" on \"%s\"", fs->fsname(), fs->dir()); fs->relocate_interests(); delete fs; } Log::debug("mtab done.");}voidFileSystemTable::destroy_fses(NameTable *fstab){ // Destroy any unreclaimed filesystems. for (unsigned i = 0; fstab->key(i); i++) delete fstab->value(i);}voidFileSystemTable::mtab_event_handler(const Event& event, void *){ if (event == Event::Changed) { Log::debug("%s changed, rebuilding filesystem table", mtab_name); fs_by_id.removeAll(); create_fs_by_name(); }}////////////////////////////////////////////////////////////////////////////////FileSystem *FileSystemTable::find(const char *path, const Cred& cr){ char temp_path[PATH_MAX]; FileSystem *fs = NULL; assert(path[0] == '/'); // (Initialize fs_by_name if necessary.) As a side effect, // create_fs_by_name initializes our "root" member variable. if (!fs_by_name) { create_fs_by_name(); mtab_watcher = new InternalClient(mtab_name, mtab_event_handler, NULL); } cr.become_user(); // If !HAVE_STATVFS, we could use statfs instead, but the statfs.f_fsid // is not set reliably on Linux, so it's useless. We'll do every lookup // by name; hopefully that doesn't suck.#if HAVE_STATVFS // Perform a statvfs(2) on the first existing ancestor. struct statvfs fs_status; int rc = statvfs(path, &fs_status); if (rc < 0) { (void) strcpy(temp_path, path); while (rc < 0) { char *slash = strrchr(temp_path, '/'); if (!slash) return root; *slash = '\0'; rc = statvfs(temp_path, &fs_status); } } // Look up filesystem by ID. fs = fs_by_id.find(fs_status.f_fsid); if (fs) return fs;#endif // Convert to real path. Look up real path in fs_by_name(). cr.become_user(); (void) realpath(path, temp_path); fs = longest_prefix(temp_path); // Insert into fs_by_id.#if HAVE_STATVFS fs_by_id.insert(fs_status.f_fsid, fs);#endif return fs;}FileSystem *FileSystemTable::longest_prefix(const char *path){ FileSystem * bestmatch = root; int maxmatch = -1; const char *key; for (unsigned i = 0; ((key = fs_by_name->key(i)) != NULL); i++) { for (int j = 0; ; j++) { if (!key[j]) { if ((!path[j] || path[j] == '/') && j > maxmatch) { maxmatch = j; bestmatch = fs_by_name->value(i); } break; } if (key[j] != path[j]) break; } } assert(bestmatch); return bestmatch;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -