[BACK]Return to FileSystemTable.c++ CVS log [TXT][DIR] Up to [Development] / fam / fam

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>