BESCatalogDirectory.cc

Go to the documentation of this file.
00001 // BESCatalogDirectory.cc
00002 
00003 // This file is part of bes, A C++ back-end server implementation framework
00004 // for the OPeNDAP Data Access Protocol.
00005 
00006 // Copyright (c) 2004-2009 University Corporation for Atmospheric Research
00007 // Author: Patrick West <pwest@ucar.edu> and Jose Garcia <jgarcia@ucar.edu>
00008 //
00009 // This library is free software; you can redistribute it and/or
00010 // modify it under the terms of the GNU Lesser General Public
00011 // License as published by the Free Software Foundation; either
00012 // version 2.1 of the License, or (at your option) any later version.
00013 //
00014 // This library is distributed in the hope that it will be useful,
00015 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00016 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00017 // Lesser General Public License for more details.
00018 //
00019 // You should have received a copy of the GNU Lesser General Public
00020 // License along with this library; if not, write to the Free Software
00021 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00022 //
00023 // You can contact University Corporation for Atmospheric Research at
00024 // 3080 Center Green Drive, Boulder, CO 80301
00025 
00026 // (c) COPYRIGHT University Corporation for Atmospheric Research 2004-2005
00027 // Please read the full copyright statement in the file COPYRIGHT_UCAR.
00028 //
00029 // Authors:
00030 //      pwest       Patrick West <pwest@ucar.edu>
00031 //      jgarcia     Jose Garcia <jgarcia@ucar.edu>
00032 
00033 #include "config.h"
00034 
00035 #include <sys/types.h>
00036 #include <sys/stat.h>
00037 #include <dirent.h>
00038 
00039 #include <cstring>
00040 #include <cerrno>
00041 #include <sstream>
00042 
00043 using std::stringstream ;
00044 using std::endl ;
00045 
00046 #include "BESCatalogDirectory.h"
00047 #include "BESCatalogUtils.h"
00048 #include "BESInfo.h"
00049 #include "BESResponseNames.h"
00050 #include "BESCatalogUtils.h"
00051 #include "BESContainerStorageList.h"
00052 #include "BESContainerStorageCatalog.h"
00053 #include "BESLog.h"
00054 #include "BESForbiddenError.h"
00055 #include "BESNotFoundError.h"
00056 #include "BESDebug.h"
00057 
00058 void bes_add_stat_info( map<string,string> &props,
00059                         struct stat &buf,
00060                         const string &node ) ;
00061 void bes_get_stat_info( BESCatalogDirectory::bes_dir_entry &entry,
00062                         struct stat &buf,
00063                         const string &node);
00064 
00065 BESCatalogDirectory::BESCatalogDirectory( const string &name )
00066     : BESCatalog( name )
00067 {
00068     _utils = BESCatalogUtils::Utils( name ) ;
00069 }
00070 
00071 BESCatalogDirectory::~BESCatalogDirectory( )
00072 {
00073 }
00074 
00075 void
00076 BESCatalogDirectory::show_catalog( const string &node,
00077                                    const string &coi,
00078                                    BESInfo *info )
00079 {
00080     // remove any trailing slash
00081 #if 0
00082     // Replaced the code below since this was causing a memory access error
00083     // flagged by valgrind when node was "/".2/25/09 jhrg
00084     string use_node = node ;
00085     if( node != "" )
00086     {
00087         string::size_type stopat = node.length() - 1 ;
00088         while( node[stopat] == '/' )
00089         {
00090             stopat-- ;
00091         }
00092         use_node = use_node.substr( 0, stopat + 1 ) ;
00093     }
00094 #else
00095     string use_node = node;
00096     // use_node should only end in '/' is that's the only character in which
00097     // case there's no need to call find()
00098     if (!node.empty() && node != "/") {
00099         string::size_type pos = use_node.find_last_not_of("/");
00100         use_node = use_node.substr(0, pos+1);
00101     }
00102 
00103     // This takes care of bizarre cases like "///" where use_node would be
00104     // empty after the substring call.
00105     if (use_node.empty())
00106         use_node = "/";
00107 
00108     BESDEBUG("bes", "use_node: " << use_node << endl )
00109 #endif
00110     string rootdir = _utils->get_root_dir() ;
00111     string fullnode = rootdir ;
00112     if( !use_node.empty() )
00113     {
00114         fullnode = fullnode + "/" + use_node ;
00115     }
00116 
00117     string basename ;
00118     string::size_type slash = fullnode.rfind( "/" ) ;
00119     if( slash != string::npos )
00120     {
00121         basename = fullnode.substr( slash+1, fullnode.length() - slash ) ;
00122     }
00123     else
00124     {
00125         basename = fullnode ;
00126     }
00127 
00128     // This will throw the appropriate exception (Forbidden or Not Found).
00129     // Checks to make sure the different elements of the path are not
00130     // symbolic links if follow_sym_links is set to false, and checks to
00131     // make sure have permission to access node and the node exists.
00132     BESUtil::check_path( use_node, rootdir, _utils->follow_sym_links() ) ;
00133 
00134     // Is this node a directory?
00135     DIR *dip = opendir( fullnode.c_str() ) ;
00136     if( dip != NULL )
00137     {
00138         // The node is a directory
00139 
00140         // if the directory requested is in the exclude list then we won't
00141         // let the user see it.
00142         if( _utils->exclude( basename ) )
00143         {
00144             closedir( dip ) ;
00145             string error = "You do not have permission to view the node "
00146                            + use_node ;
00147             throw BESForbiddenError( error, __FILE__, __LINE__ ) ;
00148         }
00149         struct stat cbuf ;
00150         int statret = stat( fullnode.c_str(), &cbuf ) ;
00151         int my_errno = errno ;
00152         if( statret == 0 )
00153         {
00154             map<string,string> props ;
00155             props["node"] = "true" ;
00156             props["catalog"] = get_catalog_name() ;
00157             if( use_node == "" )
00158             {
00159                 bes_add_stat_info( props, cbuf, "/" ) ;
00160             }
00161             else
00162             {
00163                 bes_add_stat_info( props, cbuf, use_node ) ;
00164             }
00165 
00166             struct dirent *dit;
00167             unsigned int cnt = 0 ;
00168             struct stat buf;
00169             struct stat lbuf;
00170 
00171             map<string,bes_dir_entry> dir_list ;
00172             while( ( dit = readdir( dip ) ) != NULL )
00173             {
00174                 string dirEntry = dit->d_name ;
00175                 if( dirEntry != "." && dirEntry != ".." )
00176                 {
00177                     string fullPath = fullnode + "/" + dirEntry ;
00178 
00179                     // if follow_sym_links is true then continue with
00180                     // the checking. If false, first see if the entry is
00181                     // a symbolic link. If it is, do not include in the
00182                     // listing for this node. If not, then continue
00183                     // checking the entry.
00184                     bool continue_checking = true ;
00185                     if( _utils->follow_sym_links() == false )
00186                     {
00187 #if 0
00188                         int lstatret = lstat( fullPath.c_str(), &lbuf ) ;
00189 #endif
00190                         (void)lstat( fullPath.c_str(), &lbuf ) ;
00191                         if( S_ISLNK( lbuf.st_mode ) )
00192                         {
00193                             continue_checking = false ;
00194                         }
00195                     }
00196 
00197                     if( continue_checking )
00198                     {
00199                         // look at the mode and determine if this is a
00200                         // directory or a regular file. If it is not
00201                         // accessible, the stat fails, is not a directory
00202                         // or regular file, then simply do not include it.
00203                         statret = stat( fullPath.c_str(), &buf ) ;
00204                         if ( statret == 0 && S_ISDIR( buf.st_mode ) )
00205                         {
00206                             if( _utils->exclude( dirEntry ) == false )
00207                             {
00208                                 cnt++ ;
00209                                 if( coi == CATALOG_RESPONSE )
00210                                 {
00211                                     bes_dir_entry entry ;
00212                                     entry.collection = true ;
00213                                     bes_get_stat_info( entry, buf, dirEntry ) ;
00214                                     dir_list[dirEntry] = entry ;
00215                                 }
00216                             }
00217                         }
00218                         else if ( statret == 0 && S_ISREG( buf.st_mode ) )
00219                         {
00220                             if( _utils->include( dirEntry ) )
00221                             {
00222                                 cnt++ ;
00223                                 if( coi == CATALOG_RESPONSE )
00224                                 {
00225                                     bes_dir_entry entry ;
00226                                     entry.collection = false ;
00227                                     isData( fullPath, entry.services ) ;
00228                                     bes_get_stat_info( entry, buf, dirEntry ) ;
00229                                     dir_list[dirEntry] = entry ;
00230                                 }
00231                             }
00232                         }
00233                     }
00234                 }
00235             }
00236             stringstream sscnt ;
00237             sscnt << cnt ;
00238             props["count"] = sscnt.str() ;
00239             info->begin_tag( "dataset", &props ) ;
00240 
00241             // Now iterate through the entry list and add it to info. This
00242             // will add it in alpha order
00243             if( coi == CATALOG_RESPONSE )
00244             {
00245                 map<string,bes_dir_entry>::iterator i = dir_list.begin() ;
00246                 map<string,bes_dir_entry>::iterator e = dir_list.end() ;
00247                 for( ; i != e; i++ )
00248                 {
00249                     map<string,string> attrs ;
00250                     if( (*i).second.collection )
00251                         attrs["node"] = "true" ;
00252                     else
00253                         attrs["node"] = "false" ;
00254                     attrs["catalog"] = get_catalog_name() ;
00255                     attrs["name"] = (*i).second.name ;
00256                     attrs["size"] = (*i).second.size ;
00257                     string dt = (*i).second.mod_date + "T"
00258                                 + (*i).second.mod_time ;
00259                     attrs["lastModified"] = dt ;
00260                     info->begin_tag( "dataset", &attrs ) ;
00261 
00262                     list<string>::const_iterator si =
00263                         (*i).second.services.begin() ;
00264                     list<string>::const_iterator se =
00265                         (*i).second.services.end() ;
00266                     for( ; si != se; si++ )
00267                     {
00268                         info->add_tag( "serviceRef", (*si) ) ;
00269                     }
00270                     info->end_tag( "dataset" ) ;
00271                 }
00272             }
00273             closedir( dip ) ;
00274             info->end_tag( "dataset" ) ;
00275         }
00276         else
00277         {
00278             closedir( dip ) ;
00279             // ENOENT means that the path or part of the path does not exist
00280             if( my_errno == ENOENT )
00281             {
00282                 string error = "Node " + use_node + " does not exist" ;
00283                 char *s_err = strerror( my_errno ) ;
00284                 if( s_err )
00285                 {
00286                     error = s_err ;
00287                 }
00288                 throw BESNotFoundError( error, __FILE__, __LINE__ ) ;
00289             }
00290             // any other error means that access is denied for some reason
00291             else
00292             {
00293                 string error = "Access denied for node " + use_node ;
00294                 char *s_err = strerror( my_errno ) ;
00295                 if( s_err )
00296                 {
00297                     error = error + s_err ;
00298                 }
00299                 throw BESNotFoundError( error, __FILE__, __LINE__ ) ;
00300             }
00301         }
00302     }
00303     else
00304     {
00305         // if the node is not in the include list then the requester does
00306         // not have access to that node
00307         if( _utils->include( basename ) )
00308         {
00309             struct stat buf;
00310             int statret = 0 ;
00311             if( _utils->follow_sym_links() == false )
00312             {
00313                 /*statret =*/(void)lstat( fullnode.c_str(), &buf ) ;
00314                 if( S_ISLNK( buf.st_mode ) )
00315                 {
00316                     string error = "You do not have permission to access node "
00317                                    + use_node ;
00318                     throw BESForbiddenError( error, __FILE__, __LINE__ ) ;
00319                 }
00320             }
00321             statret = stat( fullnode.c_str(), &buf ) ;
00322             if ( statret == 0 && S_ISREG( buf.st_mode ) )
00323             {
00324                 map<string,string> attrs ;
00325                 attrs["node"] = "false" ;
00326                 attrs["catalog"] = get_catalog_name() ;
00327                 bes_add_stat_info( attrs, buf, node ) ;
00328                 info->begin_tag( "dataset", &attrs ) ;
00329 
00330                 list<string> services ;
00331                 isData( node, services ) ;
00332                 list<string>::const_iterator si = services.begin() ;
00333                 list<string>::const_iterator se = services.end() ;
00334                 for( ; si != se; si++ )
00335                 {
00336                     info->add_tag( "serviceRef", (*si) ) ;
00337                 }
00338 
00339                 info->end_tag( "dataset" ) ;
00340             }
00341             else if( statret == 0 )
00342             {
00343                 string error = "You do not have permission to access "
00344                                + use_node ;
00345                 throw BESForbiddenError( error, __FILE__, __LINE__ ) ;
00346             }
00347             else
00348             {
00349                 // ENOENT means that the path or part of the path does not
00350                 // exist
00351                 if( errno == ENOENT )
00352                 {
00353                     string error = "Node " + use_node + " does not exist" ;
00354                     char *s_err = strerror( errno ) ;
00355                     if( s_err )
00356                     {
00357                         error = s_err ;
00358                     }
00359                     throw BESNotFoundError( error, __FILE__, __LINE__ ) ;
00360                 }
00361                 // any other error means that access is denied for some reason
00362                 else
00363                 {
00364                     string error = "Access denied for node " + use_node ;
00365                     char *s_err = strerror( errno ) ;
00366                     if( s_err )
00367                     {
00368                         error = error + s_err ;
00369                     }
00370                     throw BESNotFoundError( error, __FILE__, __LINE__ ) ;
00371                 }
00372             }
00373         }
00374         else
00375         {
00376             string error = "You do not have permission to access " + use_node ;
00377             throw BESForbiddenError( error, __FILE__, __LINE__ ) ;
00378         }
00379     }
00380 }
00381 
00382 void
00383 bes_add_stat_info( map<string,string> &props,
00384                    struct stat &buf,
00385                    const string &node )
00386 {
00387     BESCatalogDirectory::bes_dir_entry entry ;
00388     bes_get_stat_info( entry, buf, node ) ;
00389     props["name"] = entry.name ;
00390     props["size"] = entry.size ;
00391     string dt = entry.mod_date + "T" + entry.mod_time ;
00392     props["lastModified"] = dt ;
00393 }
00394 
00395 void
00396 bes_get_stat_info( BESCatalogDirectory::bes_dir_entry &entry,
00397                    struct stat &buf, const string &node )
00398 {
00399     entry.name = node ;
00400 
00401     off_t sz = buf.st_size ;
00402     stringstream ssz ;
00403     ssz << sz ;
00404     entry.size = ssz.str() ;
00405 
00406     // %T = %H:%M:%S
00407     // %F = %Y-%m-%d
00408     time_t mod = buf.st_mtime ;
00409     struct tm *stm = gmtime( &mod ) ;
00410     char mdate[64] ;
00411     strftime( mdate, 64, "%Y-%m-%d", stm ) ;
00412     char mtime[64] ;
00413     strftime( mtime, 64, "%T", stm ) ;
00414 
00415     stringstream sdt ;
00416     sdt << mdate ;
00417     entry.mod_date = sdt.str() ;
00418 
00419     stringstream stt ;
00420     stt << mtime ;
00421     entry.mod_time = stt.str() ;
00422 }
00423 
00424 bool
00425 BESCatalogDirectory::isData( const string &inQuestion,
00426                              list<string> &services )
00427 {
00428     BESContainerStorage *store =
00429         BESContainerStorageList::TheList()->find_persistence( get_catalog_name() ) ;
00430     if( !store )
00431         return false ;
00432 
00433     BESContainerStorageCatalog *cat_store =
00434         dynamic_cast<BESContainerStorageCatalog *>( store ) ;
00435     if( !cat_store )
00436         return false ;
00437 
00438     return cat_store->isData( inQuestion, services ) ;
00439 }
00440 
00448 void
00449 BESCatalogDirectory::dump( ostream &strm ) const
00450 {
00451     strm << BESIndent::LMarg << "BESCatalogDirectory::dump - ("
00452                              << (void *)this << ")" << endl ;
00453     BESIndent::Indent() ;
00454 
00455     strm << BESIndent::LMarg << "catalog utilities: " << endl ;
00456     BESIndent::Indent() ;
00457     _utils->dump( strm ) ;
00458     BESIndent::UnIndent() ;
00459     BESIndent::UnIndent() ;
00460 }
00461 

Generated on Sat Aug 22 06:06:21 2009 for OPeNDAP Hyrax Back End Server (BES) by  doxygen 1.6.0