bes Updated for version 3.21.1
The Backend Server (BES) is the lower two tiers of the Hyrax data server
ShowPathInfoResponseHandler.cc
1// -*- mode: c++; c-basic-offset:4 -*-
2//
3// ShowPathInfoResponseHandler.cc
4//
5// This file is part of BES dap package
6//
7// Copyright (c) 2015v OPeNDAP, Inc.
8// Author: Nathan Potter <ndp@opendap.org>
9//
10// This library is free software; you can redistribute it and/or
11// modify it under the terms of the GNU Lesser General Public
12// License as published by the Free Software Foundation; either
13// version 2.1 of the License, or (at your option) any later version.
14//
15// This library is distributed in the hope that it will be useful,
16// but WITHOUT ANY WARRANTY; without even the implied warranty of
17// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18// Lesser General Public License for more details.
19//
20// You should have received a copy of the GNU Lesser General Public
21// License along with this library; if not, write to the Free Software
22// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23//
24// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
25// Please read the full copyright statement in the file COPYRIGHT_URI.
26//
27
28#include "config.h"
29
30#include <sys/types.h>
31#include <sys/stat.h>
32#include <unistd.h>
33#include <time.h>
34
35#include <cerrno>
36#include <cstring>
37
38#include <sstream>
39#include <fstream>
40
41#include "ShowPathInfoResponseHandler.h"
42
43#include "BESDebug.h"
44
45#include "BESInfoList.h"
46#include "BESInfo.h"
47#include "BESUtil.h"
48#include "BESRequestHandlerList.h"
49#include "BESRequestHandler.h"
50#include "BESNames.h"
51#include "BESDapNames.h"
52#include "BESDataNames.h"
53#include "BESCatalogList.h"
54#include "BESCatalog.h"
55#include "BESCatalogEntry.h"
56#include "BESCatalogUtils.h"
57#include "BESSyntaxUserError.h"
58#include "BESForbiddenError.h"
59#include "BESNotFoundError.h"
60#include "BESStopWatch.h"
61
62using std::endl;
63using std::map;
64using std::string;
65using std::list;
66using std::ostream;
67
68#define PATH_INFO_RESPONSE "PathInfo"
69#define PATH "path"
70#define VALID_PATH "validPath"
71#define REMAINDER "remainder"
72#define IS_DATA "isData"
73#define IS_FILE "isFile"
74#define IS_DIR "isDir"
75#define IS_ACCESSIBLE "access"
76#define SIZE "size"
77#define LMT "lastModified"
78
79#define SPI_DEBUG_KEY "show-path-info"
80#define SHOW_PATH_INFO_RESPONSE_STR "showPathInfo"
81
82const auto MODULE="dap";
83#define prolog string("ShowPathInfoResponseHandler::").append(__func__).append("() - ")
84
85ShowPathInfoResponseHandler::ShowPathInfoResponseHandler(const string &name) :
87{
88}
89
90ShowPathInfoResponseHandler::~ShowPathInfoResponseHandler()
91{
92}
93
105{
106
107 BES_STOPWATCH_START_DHI(MODULE, prolog + "Timing", &dhi);
108
109 BESDEBUG(SPI_DEBUG_KEY,
110 "ShowPathInfoResponseHandler::execute() - BEGIN ############################################################## BEGIN" << endl);
111
112 BESInfo *info = BESInfoList::TheList()->build_info();
113 d_response_object = info;
114
115 string container = dhi.data[CONTAINER];
116#if 0
117 string catname;
118 string defcatname = BESCatalogList::TheCatalogList()->default_catalog_name();
119#endif
120
121 BESCatalog *defcat = BESCatalogList::TheCatalogList()->default_catalog();
122 if (!defcat)
123 throw BESInternalError("Not able to find the default catalog.", __FILE__, __LINE__);
124
125 // remove all of the leading slashes from the container name
126 string::size_type notslash = container.find_first_not_of("/", 0);
127 if (notslash != string::npos) {
128 container = container.substr(notslash);
129 }
130
131 // see if there is a catalog name here. It's only a possible catalog name
132 string catname;
133 string::size_type slash = container.find_first_of("/", 0);
134 if (slash != string::npos) {
135 catname = container.substr(0, slash);
136 }
137 else {
138 catname = container;
139 }
140
141 // see if this catalog exists. If it does, then remove the catalog
142 // name from the container (node)
143 BESCatalog *catobj = BESCatalogList::TheCatalogList()->find_catalog(catname);
144 if (catobj) {
145 if (slash != string::npos) {
146 container = container.substr(slash + 1);
147
148 // remove repeated slashes
149 notslash = container.find_first_not_of("/", 0);
150 if (notslash != string::npos) {
151 container = container.substr(notslash);
152 }
153 }
154 else {
155 container = "";
156 }
157 }
158
159 if (container.empty()) container = "/";
160
161 if (container[0] != '/') container = "/" + container;
162
163 BESDEBUG(SPI_DEBUG_KEY, "ShowPathInfoResponseHandler::execute() - container: " << container << endl);
164
165 info->begin_response(SHOW_PATH_INFO_RESPONSE_STR, dhi);
166 //string coi = dhi.data[CATALOG_OR_INFO];
167
168 map<string, string, std::less<>> pathInfoAttrs;
169 pathInfoAttrs[PATH] = container;
170
171 info->begin_tag(PATH_INFO_RESPONSE, &pathInfoAttrs);
172
173 string validPath, remainder;
174 bool isFile, isDir, canRead;
175 long long size, time;
176
177 BESCatalogUtils *utils = BESCatalogList::TheCatalogList()->default_catalog()->get_catalog_utils();
178
179 eval_resource_path(container, utils->get_root_dir(), utils->follow_sym_links(), validPath, isFile, isDir, size,
180 time, canRead, remainder);
181
182 // Now that we know what part of the path is actually something
183 // we can access, find out if the BES sees it as a dataset
184 bool isData = false;
185
186 // If the valid path is an empty string then we KNOW it's not a dataset
187 if (validPath.size() != 0) {
188
189 // Get the catalog entry.
190 BESCatalogEntry *entry = 0;
191 //string coi = dhi.data[CATALOG];
192 entry = defcat->show_catalog(validPath, /*coi,*/entry);
193 if (!entry) {
194 string err = (string) "Failed to find the validPath node " + validPath
195 + " this should not be possible. Some thing BAD is happening.";
196 throw BESInternalError(err, __FILE__, __LINE__);
197 }
198
199 // Retrieve the valid services list
200 list<string> services = entry->get_service_list();
201
202 // See if there's an OPENDAP_SERVICE available for the node.
203 if (services.size()) {
204 list<string>::const_iterator si = services.begin();
205 list<string>::const_iterator se = services.end();
206 for (; si != se; si++) {
207 if ((*si) == OPENDAP_SERVICE) isData = true;
208 }
209 }
210 }
211
212 map<string, string, std::less<>> validPathAttrs;
213 validPathAttrs[IS_DATA] = isData ? "true" : "false";
214 validPathAttrs[IS_FILE] = isFile ? "true" : "false";
215 validPathAttrs[IS_DIR] = isDir ? "true" : "false";
216 validPathAttrs[IS_ACCESSIBLE] = canRead ? "true" : "false";
217
218 // Convert size to string and add as attribute
219 std::ostringstream os_size;
220 os_size << size;
221 validPathAttrs[SIZE] = os_size.str();
222
223 // Convert lmt to string and add as attribute
224 std::ostringstream os_time;
225 os_time << time;
226 validPathAttrs[LMT] = os_time.str();
227
228 info->add_tag(VALID_PATH, validPath, &validPathAttrs);
229 info->add_tag(REMAINDER, remainder);
230
231 info->end_tag(PATH_INFO_RESPONSE);
232
233 // end the response object
234 info->end_response();
235
236 BESDEBUG(SPI_DEBUG_KEY, "ShowPathInfoResponseHandler::execute() - END" << endl);
237}
238
251{
252 if (d_response_object) {
253 BESInfo *info = dynamic_cast<BESInfo *>(d_response_object);
254 if (!info) throw BESInternalError("cast error", __FILE__, __LINE__);
255 info->transmit(transmitter, dhi);
256 }
257}
258
265void ShowPathInfoResponseHandler::dump(ostream &strm) const
266{
267 strm << BESIndent::LMarg << "ShowPathInfoResponseHandler::dump - (" << (void *) this << ")" << std::endl;
268 BESIndent::Indent();
270 BESIndent::UnIndent();
271}
272
274ShowPathInfoResponseHandler::ShowPathInfoResponseBuilder(const string &name)
275{
276 return new ShowPathInfoResponseHandler(name);
277}
278
282void ShowPathInfoResponseHandler::eval_resource_path(const string &resource_path, const string &catalog_root,
283 const bool follow_sym_links, string &validPath, bool &isFile, bool &isDir, long long &size,
284 long long &lastModifiedTime, bool &canRead, string &remainder)
285{
286
287 BESDEBUG(SPI_DEBUG_KEY,
288 "ShowPathInfoResponseHandler::"<<__func__ << "() - " << "CatalogRoot: "<< catalog_root << endl);
289
290 BESDEBUG(SPI_DEBUG_KEY,
291 "ShowPathInfoResponseHandler::"<<__func__ << "() - " << "resourceID: "<< resource_path << endl);
292
293 // nothing valid yet...
294 validPath = "";
295 size = -1;
296 lastModifiedTime = -1;
297
298 // It's all remainder at this point...
299 string rem = resource_path;
300 remainder = rem;
301
302 // Rather than have two basically identical code paths for the two cases (follow and !follow symlinks)
303 // We evaluate the follow_sym_links switch and use a function pointer to get the correct "stat"
304 // function for the eval operation.
305 int (*ye_old_stat_function)(const char *pathname, struct stat *buf);
306 if (follow_sym_links) {
307 BESDEBUG(SPI_DEBUG_KEY,
308 "ShowPathInfoResponseHandler::"<<__func__ << "() - " << "Using 'stat' function (follow_sym_links = true)" << endl);
309 ye_old_stat_function = &stat;
310 }
311 else {
312 BESDEBUG(SPI_DEBUG_KEY,
313 "ShowPathInfoResponseHandler::"<<__func__ << "() - " << "Using 'lstat' function (follow_sym_links = false)" << endl);
314 ye_old_stat_function = &lstat;
315 }
316
317 // if nothing is passed in path, then the path checks out since root is
318 // assumed to be valid.
319 if (resource_path == "") {
320 BESDEBUG(SPI_DEBUG_KEY, "ShowPathInfoResponseHandler::"<<__func__ << "() - The resourceID is empty" << endl);
321 return;
322 }
323
324 // make sure there are no ../ in the path, backing up in any way is
325 // not allowed.
326 string::size_type dotdot = resource_path.find("..");
327 if (dotdot != string::npos) {
328 BESDEBUG(SPI_DEBUG_KEY,
329 "ShowPathInfoResponseHandler::"<<__func__ << "() - " << " ERROR: The resourceID '" << resource_path <<"' contains the substring '..' This is Forbidden." << endl);
330 string s = (string) "Invalid node name '" + resource_path + "' ACCESS IS FORBIDDEN";
331
332 throw BESForbiddenError(s, __FILE__, __LINE__);
333 }
334
335 // What I want to do is to take each part of path and check to see if it
336 // is a symbolic link and it is accessible. If everything is ok, add the
337 // next part of the path.
338 bool done = false;
339
340 // Full file system path to check
341 string fullpath = catalog_root;
342
343 // localId that we are checking
344 string checking;
345
346 isFile = false;
347 isDir = false;
348
349 while (!done) {
350 size_t slash = rem.find('/');
351 if (slash == string::npos) {
352 BESDEBUG(SPI_DEBUG_KEY,
353 "ShowPathInfoResponseHandler::"<<__func__ << "() - " << "Checking final path component: " << rem << endl);
354 fullpath = BESUtil::assemblePath(fullpath, rem, true);
355 checking = BESUtil::assemblePath(validPath, rem, true);
356 rem = "";
357 done = true;
358 }
359 else {
360 fullpath = BESUtil::assemblePath(fullpath, rem.substr(0, slash), true);
361 checking = BESUtil::assemblePath(validPath, rem.substr(0, slash), true);
362 rem = rem.substr(slash + 1, rem.size() - slash);
363 }
364
365 BESDEBUG(SPI_DEBUG_KEY,
366 "ShowPathInfoResponseHandler::"<<__func__ << "() - " << "validPath: "<< validPath << endl);
367 BESDEBUG(SPI_DEBUG_KEY,
368 "ShowPathInfoResponseHandler::"<<__func__ << "() - " << "checking: "<< checking << endl);
369 BESDEBUG(SPI_DEBUG_KEY,
370 "ShowPathInfoResponseHandler::"<<__func__ << "() - " << "fullpath: "<< fullpath << endl);
371
372 BESDEBUG(SPI_DEBUG_KEY, "ShowPathInfoResponseHandler::"<<__func__ << "() - " << "rem: "<< rem << endl);
373
374 BESDEBUG(SPI_DEBUG_KEY,
375 "ShowPathInfoResponseHandler::"<<__func__ << "() - " << "remainder: "<< remainder << endl);
376
377 struct stat sb;
378 int statret = ye_old_stat_function(fullpath.c_str(), &sb);
379
380 if (statret != -1) {
381 // No Error then keep chugging along.
382 validPath = checking;
383 remainder = rem;
384 }
385 else {
386 int errsv = errno;
387 // stat failed, so not accessible. Get the error string,
388 // store in error, and throw exception
389 char *s_err = strerror(errsv);
390 string error = "Unable to access node " + checking + ": ";
391 if (s_err) {
392 error = error + s_err;
393 }
394 else {
395 error = error + "unknown access error";
396 }
397 BESDEBUG(SPI_DEBUG_KEY,
398 "ShowPathInfoResponseHandler::"<<__func__ << "() - " << "error: "<< error << " errno: " << errno << endl);
399
400 BESDEBUG(SPI_DEBUG_KEY,
401 "ShowPathInfoResponseHandler::"<<__func__ << "() - " << "remainder: '" << remainder << "'" << endl);
402
403 // ENOENT means that the node wasn't found. Otherwise, access
404 // is denied for some reason
405 if (errsv != ENOENT && errsv != ENOTDIR) {
406 throw BESForbiddenError(error, __FILE__, __LINE__);
407 }
408
409 // Are there slashes in the remainder?
410 size_t s_loc = remainder.find('/');
411 if (s_loc == string::npos) {
412 // if there are no more slashes, we check to see if this final path component contains "."
413 string basename = remainder;
414 bool moreDots = true;
415 while (moreDots) {
416 // working back from end of string, drop each dot (".") suffix until file system match or string gone
417 size_t d_loc = basename.find_last_of(".");
418 if (d_loc != string::npos) {
419 basename = basename.substr(0, d_loc);
420 BESDEBUG(SPI_DEBUG_KEY,
421 "ShowPathInfoResponseHandler::" << __func__ << "() - basename: "<< basename << endl);
422
423 string candidate_remainder = remainder.substr(basename.size());
424 BESDEBUG(SPI_DEBUG_KEY,
425 "ShowPathInfoResponseHandler::" << __func__ << "() - candidate_remainder: "<< candidate_remainder << endl);
426
427 string candidate_path = BESUtil::assemblePath(validPath, basename, true);
428 BESDEBUG(SPI_DEBUG_KEY,
429 "ShowPathInfoResponseHandler::" << __func__ << "() - candidate_path: "<< candidate_path << endl);
430
431 string full_candidate_path = BESUtil::assemblePath(catalog_root, candidate_path, true);
432 BESDEBUG(SPI_DEBUG_KEY,
433 "ShowPathInfoResponseHandler::" << __func__ << "() - full_candidate_path: "<< full_candidate_path << endl);
434
435 struct stat sb1;
436 int statret1 = ye_old_stat_function(full_candidate_path.c_str(), &sb1);
437 if (statret1 != -1) {
438 validPath = candidate_path;
439 remainder = candidate_remainder;
440 moreDots = false;
441 }
442 }
443 else {
444 BESDEBUG(SPI_DEBUG_KEY,
445 "ShowPathInfoResponseHandler::"<<__func__ << "() - " << "No dots in remainder: "<< remainder << endl);
446 moreDots = false;
447 }
448 }
449 }
450 else {
451 BESDEBUG(SPI_DEBUG_KEY,
452 "ShowPathInfoResponseHandler::"<<__func__ << "() - " << "Remainder has slash pollution: "<< remainder << endl);
453 done = true;
454 }
455 }
456 fullpath = BESUtil::assemblePath(catalog_root, validPath, true);
457
458 statret = ye_old_stat_function(fullpath.c_str(), &sb);
459 if (S_ISREG(sb.st_mode)) {
460 BESDEBUG(SPI_DEBUG_KEY,
461 "ShowPathInfoResponseHandler::"<<__func__ << "() - " << "'"<< fullpath << "' Is regular file." << endl);
462 isFile = true;
463 isDir = false;
464 }
465 else if (S_ISDIR(sb.st_mode)) {
466 BESDEBUG(SPI_DEBUG_KEY,
467 "ShowPathInfoResponseHandler::"<<__func__ << "() - " << "'"<< fullpath << "' Is directory." << endl);
468 isFile = false;
469 isDir = true;
470 }
471 else if (S_ISLNK(sb.st_mode)) {
472 BESDEBUG(SPI_DEBUG_KEY,
473 "ShowPathInfoResponseHandler::"<<__func__ << "() - " << "'"<< fullpath << "' Is symbolic Link." << endl);
474 string error = "Service not configured to traverse symbolic links as embodied by the node '" + checking
475 + "' ACCESS IS FORBIDDEN";
476 throw BESForbiddenError(error, __FILE__, __LINE__);
477 }
478 // sb.st_uid;
479 // sb.st_uid;
480
481 // Can we read le file?
482 std::ifstream ifile(fullpath.c_str());
483 canRead = ifile.good();
484
485 size = sb.st_size;
486
487 // I'm pretty sure that assigning st_mtime to a long long (when it is a time_t) is not a
488 // good plan - time_t is either a 32- or 64-bit signed integer.
489 //
490 // But, see ESCatalogUtils::bes_get_stat_info(BESCatalogEntry *entry, struct stat &buf)
491 // for code that probably does a more universal version. of this (and other things relative
492 // to stat, like symbolic link following).
493 //
494 // I added this #if ... #endif because Linux does not have st_mtimespec in struct stat.
495 // jhrg 2.24.18
496#if !defined(_POSIX_C_SOURCE) || defined(_DARWIN_C_SOURCE)
497 // Compute LMT by converting the time to milliseconds since epoch - because OLFS is picky
498 lastModifiedTime = (sb.st_mtimespec.tv_sec * 1000) + (sb.st_mtimespec.tv_nsec / 1000000);
499#else
500 lastModifiedTime = sb.st_mtime;
501#endif
502 }
503
504 BESDEBUG(SPI_DEBUG_KEY, "ShowPathInfoResponseHandler::" << __func__ << "() - fullpath: " << fullpath << endl);
505 BESDEBUG(SPI_DEBUG_KEY, "ShowPathInfoResponseHandler::" << __func__ << "() - validPath: " << validPath << endl);
506 BESDEBUG(SPI_DEBUG_KEY, "ShowPathInfoResponseHandler::" << __func__ << "() - remainder: " << remainder << endl);
507 BESDEBUG(SPI_DEBUG_KEY, "ShowPathInfoResponseHandler::" << __func__ << "() - rem: " << rem << endl);
508 BESDEBUG(SPI_DEBUG_KEY,
509 "ShowPathInfoResponseHandler::" << __func__ << "() - isFile: " << (isFile?"true":"false") << endl);
510 BESDEBUG(SPI_DEBUG_KEY,
511 "ShowPathInfoResponseHandler::" << __func__ << "() - isDir: " << (isDir?"true":"false") << endl);
512 BESDEBUG(SPI_DEBUG_KEY,
513 "ShowPathInfoResponseHandler::" << __func__ << "() - access: " << (canRead?"true":"false") << endl);
514 BESDEBUG(SPI_DEBUG_KEY, "ShowPathInfoResponseHandler::" << __func__ << "() - size: " << size << endl);
515 BESDEBUG(SPI_DEBUG_KEY,
516 "ShowPathInfoResponseHandler::" << __func__ << "() - LMT: " << lastModifiedTime << endl);
517}
const std::string & get_root_dir() const
Get the root directory of the catalog.
Catalogs provide a hierarchical organization for data.
Definition BESCatalog.h:51
virtual BESCatalogEntry * show_catalog(const std::string &container, BESCatalogEntry *entry)=0
Structure storing information used by the BES to handle the request.
std::map< std::string, std::string > data
the map of string data that will be required for the current request.
informational response object
Definition BESInfo.h:63
virtual void transmit(BESTransmitter *transmitter, BESDataHandlerInterface &dhi)=0
transmit the informational object
virtual void begin_response(const std::string &response_name, BESDataHandlerInterface &dhi)
begin the informational response
Definition BESInfo.cc:120
exception thrown if internal error encountered
handler object that knows how to create a specific response object
void dump(std::ostream &strm) const override
dumps information about this object
static std::string assemblePath(const std::string &firstPart, const std::string &secondPart, bool leadingSlash=false, bool trailingSlash=false)
Assemble path fragments making sure that they are separated by a single '/' character.
Definition BESUtil.cc:804
response handler that returns nodes or leaves within the catalog either at the root or at a specified...
virtual void transmit(BESTransmitter *transmitter, BESDataHandlerInterface &dhi)
transmit the response object built by the execute command using the specified transmitter object
virtual void execute(BESDataHandlerInterface &dhi)
executes the command 'show catalog|leaves [for <node>];' by returning nodes or leaves at the top leve...
virtual void dump(std::ostream &strm) const
dumps information about this object
STL iterator class.