OPeNDAP Hyrax Back End Server (BES)  Updated for version 3.8.3
BESCatalogDirectory.cc
Go to the documentation of this file.
1 // BESCatalogDirectory.cc
2 
3 // This file is part of bes, A C++ back-end server implementation framework
4 // for the OPeNDAP Data Access Protocol.
5 
6 // Copyright (c) 2004-2009 University Corporation for Atmospheric Research
7 // Author: Patrick West <pwest@ucar.edu> and Jose Garcia <jgarcia@ucar.edu>
8 //
9 // This library is free software; you can redistribute it and/or
10 // modify it under the terms of the GNU Lesser General Public
11 // License as published by the Free Software Foundation; either
12 // version 2.1 of the License, or (at your option) any later version.
13 //
14 // This library is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 // Lesser General Public License for more details.
18 //
19 // You should have received a copy of the GNU Lesser General Public
20 // License along with this library; if not, write to the Free Software
21 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 //
23 // You can contact University Corporation for Atmospheric Research at
24 // 3080 Center Green Drive, Boulder, CO 80301
25 
26 // (c) COPYRIGHT University Corporation for Atmospheric Research 2004-2005
27 // Please read the full copyright statement in the file COPYRIGHT_UCAR.
28 //
29 // Authors:
30 // pwest Patrick West <pwest@ucar.edu>
31 // jgarcia Jose Garcia <jgarcia@ucar.edu>
32 
33 #include "config.h"
34 
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 #include <dirent.h>
38 
39 #include <cstring>
40 #include <cerrno>
41 #include <sstream>
42 
43 using std::stringstream ;
44 using std::endl ;
45 
46 #include "BESCatalogDirectory.h"
47 #include "BESCatalogUtils.h"
48 #include "BESInfo.h"
49 #include "BESDapNames.h"
50 #include "BESCatalogUtils.h"
53 #include "BESLog.h"
54 #include "BESForbiddenError.h"
55 #include "BESNotFoundError.h"
56 #include "BESDebug.h"
57 
58 void bes_add_stat_info( map<string,string> &props,
59  struct stat &buf,
60  const string &node ) ;
62  struct stat &buf,
63  const string &node);
64 
66  : BESCatalog( name )
67 {
68  _utils = BESCatalogUtils::Utils( name ) ;
69 }
70 
72 {
73 }
74 
75 void
77  const string &coi,
78  BESInfo *info )
79 {
80  // remove any trailing slash
81 #if 0
82  // Replaced the code below since this was causing a memory access error
83  // flagged by valgrind when node was "/".2/25/09 jhrg
84  string use_node = node ;
85  if( node != "" )
86  {
87  string::size_type stopat = node.length() - 1 ;
88  while( node[stopat] == '/' )
89  {
90  stopat-- ;
91  }
92  use_node = use_node.substr( 0, stopat + 1 ) ;
93  }
94 #else
95  string use_node = node;
96  // use_node should only end in '/' is that's the only character in which
97  // case there's no need to call find()
98  if (!node.empty() && node != "/") {
99  string::size_type pos = use_node.find_last_not_of("/");
100  use_node = use_node.substr(0, pos+1);
101  }
102 
103  // This takes care of bizarre cases like "///" where use_node would be
104  // empty after the substring call.
105  if (use_node.empty())
106  use_node = "/";
107 
108  BESDEBUG("bes", "use_node: " << use_node << endl ) ;
109 #endif
110  string rootdir = _utils->get_root_dir() ;
111  string fullnode = rootdir ;
112  if( !use_node.empty() )
113  {
114  fullnode = fullnode + "/" + use_node ;
115  }
116 
117  string basename ;
118  string::size_type slash = fullnode.rfind( "/" ) ;
119  if( slash != string::npos )
120  {
121  basename = fullnode.substr( slash+1, fullnode.length() - slash ) ;
122  }
123  else
124  {
125  basename = fullnode ;
126  }
127 
128  // This will throw the appropriate exception (Forbidden or Not Found).
129  // Checks to make sure the different elements of the path are not
130  // symbolic links if follow_sym_links is set to false, and checks to
131  // make sure have permission to access node and the node exists.
132  BESUtil::check_path( use_node, rootdir, _utils->follow_sym_links() ) ;
133 
134  // Is this node a directory?
135  DIR *dip = opendir( fullnode.c_str() ) ;
136  if( dip != NULL )
137  {
138  // The node is a directory
139 
140  // if the directory requested is in the exclude list then we won't
141  // let the user see it.
142  if( _utils->exclude( basename ) )
143  {
144  closedir( dip ) ;
145  string error = "You do not have permission to view the node "
146  + use_node ;
147  throw BESForbiddenError( error, __FILE__, __LINE__ ) ;
148  }
149  struct stat cbuf ;
150  int statret = stat( fullnode.c_str(), &cbuf ) ;
151  int my_errno = errno ;
152  if( statret == 0 )
153  {
154  map<string,string> props ;
155  props["node"] = "true" ;
156  props["catalog"] = get_catalog_name() ;
157  if( use_node == "" )
158  {
159  bes_add_stat_info( props, cbuf, "/" ) ;
160  }
161  else
162  {
163  bes_add_stat_info( props, cbuf, use_node ) ;
164  }
165 
166  struct dirent *dit;
167  unsigned int cnt = 0 ;
168  struct stat buf;
169  struct stat lbuf;
170 
171  map<string,bes_dir_entry> dir_list ;
172  while( ( dit = readdir( dip ) ) != NULL )
173  {
174  string dirEntry = dit->d_name ;
175  if( dirEntry != "." && dirEntry != ".." )
176  {
177  string fullPath = fullnode + "/" + dirEntry ;
178 
179  // if follow_sym_links is true then continue with
180  // the checking. If false, first see if the entry is
181  // a symbolic link. If it is, do not include in the
182  // listing for this node. If not, then continue
183  // checking the entry.
184  bool continue_checking = true ;
185  if( _utils->follow_sym_links() == false )
186  {
187 #if 0
188  int lstatret = lstat( fullPath.c_str(), &lbuf ) ;
189 #endif
190  (void)lstat( fullPath.c_str(), &lbuf ) ;
191  if( S_ISLNK( lbuf.st_mode ) )
192  {
193  continue_checking = false ;
194  }
195  }
196 
197  if( continue_checking )
198  {
199  // look at the mode and determine if this is a
200  // directory or a regular file. If it is not
201  // accessible, the stat fails, is not a directory
202  // or regular file, then simply do not include it.
203  statret = stat( fullPath.c_str(), &buf ) ;
204  if ( statret == 0 && S_ISDIR( buf.st_mode ) )
205  {
206  if( _utils->exclude( dirEntry ) == false )
207  {
208  cnt++ ;
209  if( coi == CATALOG_RESPONSE )
210  {
211  bes_dir_entry entry ;
212  entry.collection = true ;
213  bes_get_stat_info( entry, buf, dirEntry ) ;
214  dir_list[dirEntry] = entry ;
215  }
216  }
217  }
218  else if ( statret == 0 && S_ISREG( buf.st_mode ) )
219  {
220  if( _utils->include( dirEntry ) )
221  {
222  cnt++ ;
223  if( coi == CATALOG_RESPONSE )
224  {
225  bes_dir_entry entry ;
226  entry.collection = false ;
227  isData( fullPath, entry.services ) ;
228  bes_get_stat_info( entry, buf, dirEntry ) ;
229  dir_list[dirEntry] = entry ;
230  }
231  }
232  }
233  }
234  }
235  }
236  stringstream sscnt ;
237  sscnt << cnt ;
238  props["count"] = sscnt.str() ;
239  info->begin_tag( "dataset", &props ) ;
240 
241  // Now iterate through the entry list and add it to info. This
242  // will add it in alpha order
243  if( coi == CATALOG_RESPONSE )
244  {
245  map<string,bes_dir_entry>::iterator i = dir_list.begin() ;
246  map<string,bes_dir_entry>::iterator e = dir_list.end() ;
247  for( ; i != e; i++ )
248  {
249  map<string,string> attrs ;
250  if( (*i).second.collection )
251  attrs["node"] = "true" ;
252  else
253  attrs["node"] = "false" ;
254  attrs["catalog"] = get_catalog_name() ;
255  attrs["name"] = (*i).second.name ;
256  attrs["size"] = (*i).second.size ;
257  string dt = (*i).second.mod_date + "T"
258  + (*i).second.mod_time ;
259  attrs["lastModified"] = dt ;
260  info->begin_tag( "dataset", &attrs ) ;
261 
262  list<string>::const_iterator si =
263  (*i).second.services.begin() ;
264  list<string>::const_iterator se =
265  (*i).second.services.end() ;
266  for( ; si != se; si++ )
267  {
268  info->add_tag( "serviceRef", (*si) ) ;
269  }
270  info->end_tag( "dataset" ) ;
271  }
272  }
273  closedir( dip ) ;
274  info->end_tag( "dataset" ) ;
275  }
276  else
277  {
278  closedir( dip ) ;
279  // ENOENT means that the path or part of the path does not exist
280  if( my_errno == ENOENT )
281  {
282  string error = "Node " + use_node + " does not exist" ;
283  char *s_err = strerror( my_errno ) ;
284  if( s_err )
285  {
286  error = s_err ;
287  }
288  throw BESNotFoundError( error, __FILE__, __LINE__ ) ;
289  }
290  // any other error means that access is denied for some reason
291  else
292  {
293  string error = "Access denied for node " + use_node ;
294  char *s_err = strerror( my_errno ) ;
295  if( s_err )
296  {
297  error = error + s_err ;
298  }
299  throw BESNotFoundError( error, __FILE__, __LINE__ ) ;
300  }
301  }
302  }
303  else
304  {
305  // if the node is not in the include list then the requester does
306  // not have access to that node
307  if( _utils->include( basename ) )
308  {
309  struct stat buf;
310  int statret = 0 ;
311  if( _utils->follow_sym_links() == false )
312  {
313  /*statret =*/(void)lstat( fullnode.c_str(), &buf ) ;
314  if( S_ISLNK( buf.st_mode ) )
315  {
316  string error = "You do not have permission to access node "
317  + use_node ;
318  throw BESForbiddenError( error, __FILE__, __LINE__ ) ;
319  }
320  }
321  statret = stat( fullnode.c_str(), &buf ) ;
322  if ( statret == 0 && S_ISREG( buf.st_mode ) )
323  {
324  map<string,string> attrs ;
325  attrs["node"] = "false" ;
326  attrs["catalog"] = get_catalog_name() ;
327  bes_add_stat_info( attrs, buf, node ) ;
328  info->begin_tag( "dataset", &attrs ) ;
329 
330  list<string> services ;
331  isData( node, services ) ;
332  list<string>::const_iterator si = services.begin() ;
333  list<string>::const_iterator se = services.end() ;
334  for( ; si != se; si++ )
335  {
336  info->add_tag( "serviceRef", (*si) ) ;
337  }
338 
339  info->end_tag( "dataset" ) ;
340  }
341  else if( statret == 0 )
342  {
343  string error = "You do not have permission to access "
344  + use_node ;
345  throw BESForbiddenError( error, __FILE__, __LINE__ ) ;
346  }
347  else
348  {
349  // ENOENT means that the path or part of the path does not
350  // exist
351  if( errno == ENOENT )
352  {
353  string error = "Node " + use_node + " does not exist" ;
354  char *s_err = strerror( errno ) ;
355  if( s_err )
356  {
357  error = s_err ;
358  }
359  throw BESNotFoundError( error, __FILE__, __LINE__ ) ;
360  }
361  // any other error means that access is denied for some reason
362  else
363  {
364  string error = "Access denied for node " + use_node ;
365  char *s_err = strerror( errno ) ;
366  if( s_err )
367  {
368  error = error + s_err ;
369  }
370  throw BESNotFoundError( error, __FILE__, __LINE__ ) ;
371  }
372  }
373  }
374  else
375  {
376  string error = "You do not have permission to access " + use_node ;
377  throw BESForbiddenError( error, __FILE__, __LINE__ ) ;
378  }
379  }
380 }
381 
382 void
383 bes_add_stat_info( map<string,string> &props,
384  struct stat &buf,
385  const string &node )
386 {
388  bes_get_stat_info( entry, buf, node ) ;
389  props["name"] = entry.name ;
390  props["size"] = entry.size ;
391  string dt = entry.mod_date + "T" + entry.mod_time ;
392  props["lastModified"] = dt ;
393 }
394 
395 void
397  struct stat &buf, const string &node )
398 {
399  entry.name = node ;
400 
401  off_t sz = buf.st_size ;
402  stringstream ssz ;
403  ssz << sz ;
404  entry.size = ssz.str() ;
405 
406  // %T = %H:%M:%S
407  // %F = %Y-%m-%d
408  time_t mod = buf.st_mtime ;
409  struct tm *stm = gmtime( &mod ) ;
410  char mdate[64] ;
411  strftime( mdate, 64, "%Y-%m-%d", stm ) ;
412  char mtime[64] ;
413  strftime( mtime, 64, "%T", stm ) ;
414 
415  stringstream sdt ;
416  sdt << mdate ;
417  entry.mod_date = sdt.str() ;
418 
419  stringstream stt ;
420  stt << mtime ;
421  entry.mod_time = stt.str() ;
422 }
423 
424 bool
425 BESCatalogDirectory::isData( const string &inQuestion,
426  list<string> &services )
427 {
428  BESContainerStorage *store =
430  if( !store )
431  return false ;
432 
433  BESContainerStorageCatalog *cat_store =
434  dynamic_cast<BESContainerStorageCatalog *>( store ) ;
435  if( !cat_store )
436  return false ;
437 
438  return cat_store->isData( inQuestion, services ) ;
439 }
440 
448 void
449 BESCatalogDirectory::dump( ostream &strm ) const
450 {
451  strm << BESIndent::LMarg << "BESCatalogDirectory::dump - ("
452  << (void *)this << ")" << endl ;
454 
455  strm << BESIndent::LMarg << "catalog utilities: " << endl ;
457  _utils->dump( strm ) ;
460 }
461