1
0
Fork 0

Add-ons: new Addon methods to access the storage area beneath $FG_HOME/Export

New methods Addon::createStorageDir() and Addon::getStoragePath() with
corresponding Nasal bindings in the addons.Addon ghost:

  createStorageDir() method (returns the dir, doesn't fail if it already
                     exists)
  storagePath        read-only attribute to get the dir

The directory reserved for each add-on is
$FG_HOME/Export/Addons/ADDON_ID, but please use the above methods (or
the corresponding C++ ones) to avoid hardcoding such paths in your code.

Also create directory $FG_HOME/Export/Addons in fgInitConfig() as a way
of reserving the namespace, in order to prevent future failures in case
someone would have the strange idea to create it as a file...
This commit is contained in:
Florent Rougon 2018-03-11 08:25:38 +01:00
parent 10caef48b1
commit 9a044a474b
5 changed files with 80 additions and 1 deletions

View file

@ -24,6 +24,7 @@
#include <utility>
#include <vector>
#include <simgear/misc/sg_dir.hxx>
#include <simgear/misc/sg_path.hxx>
#include <simgear/misc/strutils.hxx>
#include <simgear/nasal/cppbind/Ghost.hxx>
@ -34,6 +35,7 @@
#include <Main/fg_props.hxx>
#include <Main/globals.hxx>
#include <Main/util.hxx>
#include <Scripting/NasalSys.hxx>
#include "addon_fwd.hxx"
@ -93,6 +95,7 @@ Addon::Addon(std::string id, AddonVersion version, SGPath basePath,
_version(
shared_ptr_traits<AddonVersionRef>::makeStrongRef(std::move(version))),
_basePath(std::move(basePath)),
_storagePath(globals->get_fg_home() / ("Export/Addons/" + _id)),
_minFGVersionRequired(std::move(minFGVersionRequired)),
_maxFGVersionRequired(std::move(maxFGVersionRequired)),
_addonNode(addonNode)
@ -187,6 +190,48 @@ SGPath Addon::getBasePath() const
void Addon::setBasePath(const SGPath& addonBasePath)
{ _basePath = addonBasePath; }
SGPath Addon::getStoragePath() const
{ return _storagePath; }
SGPath Addon::createStorageDir() const
{
if (_storagePath.exists()) {
if (!_storagePath.isDir()) {
string msg =
"Unable to create add-on storage directory because the entry already "
"exists, but is not a directory: '" + _storagePath.utf8Str() + "'";
// Log + throw, because if called from Nasal, only throwing would cause
// the exception message to 1) be truncated and 2) only appear in the
// log, not stopping the sim. Then users would have to figure out why
// their add-on doesn't work...
SG_LOG(SG_GENERAL, SG_POPUP, msg);
throw errors::unable_to_create_addon_storage_dir(msg);
}
} else {
SGPath authorizedPath = fgValidatePath(_storagePath, true /* write */);
if (authorizedPath.isNull()) {
string msg =
"Unable to create add-on storage directory because of the FlightGear "
"security policy (refused by fgValidatePath()): '" +
_storagePath.utf8Str() + "'";
SG_LOG(SG_GENERAL, SG_POPUP, msg);
throw errors::unable_to_create_addon_storage_dir(msg);
} else {
simgear::Dir(authorizedPath).create(0777);
}
}
// The sensitive operation (creating the directory) is behind us; return
// _storagePath instead of authorizedPath for consistency with the
// getStoragePath() method (_storagePath and authorizedPath could be
// different in case the former contains symlink components). Further
// sensitive operations beneath _storagePath must use fgValidatePath() again
// every time, of course (otherwise attackers could use symlinks in
// _storagePath to bypass the security policy).
return _storagePath;
}
std::string Addon::resourcePath(const std::string& relativePath) const
{
if (strutils::starts_with(relativePath, "/")) {
@ -448,6 +493,8 @@ void Addon::setupGhost(nasal::Hash& addonsModule)
.member("licenseUrl", &Addon::getLicenseUrl)
.member("tags", &Addon::getTags)
.member("basePath", &Addon::getBasePath)
.member("storagePath", &Addon::getStoragePath)
.method("createStorageDir", &Addon::createStorageDir)
.method("resourcePath", &Addon::resourcePath)
.member("minFGVersionRequired", &Addon::getMinFGVersionRequired)
.member("maxFGVersionRequired", &Addon::getMaxFGVersionRequired)

View file

@ -127,6 +127,13 @@ public:
SGPath getBasePath() const;
void setBasePath(const SGPath& addonBasePath);
// Return $FG_HOME/Export/Addons/ADDON_ID as an SGPath instance.
SGPath getStoragePath() const;
// Create directory $FG_HOME/Export/Addons/ADDON_ID, including any parent,
// if it doesn't already exist. Throw an exception in case of problems.
// Return an SGPath instance for the directory (same as getStoragePath()).
SGPath createStorageDir() const;
// Return a resource path suitable for use with the simgear::ResourceManager.
// 'relativePath' is relative to the add-on base path, and should not start
// with a '/'.
@ -218,6 +225,8 @@ private:
std::vector<std::string> _tags;
SGPath _basePath;
// $FG_HOME/Export/Addons/ADDON_ID
const SGPath _storagePath;
// To be used with simgear::strutils::compare_versions()
std::string _minFGVersionRequired;

View file

@ -61,6 +61,7 @@ class duplicate_registration_attempt;
class fg_version_too_old;
class fg_version_too_recent;
class invalid_resource_path;
class unable_to_create_addon_storage_dir;
} // of namespace errors

View file

@ -65,6 +65,9 @@ class fg_version_too_recent : public error
class invalid_resource_path : public error
{ using error::error; };
class unable_to_create_addon_storage_dir : public error
{ using error::error; };
} // of namespace errors
} // of namespace addons

View file

@ -488,6 +488,21 @@ bool fgInitHome()
return result;
}
static void createBaseStorageDirForAddons(const SGPath& exportDir)
{
SGPath addonStorageBasePath = exportDir / "Addons";
if (addonStorageBasePath.exists()) {
if (!addonStorageBasePath.isDir()) {
throw sg_error(
"Unable to create add-on storage base directory, because the entry "
"already exists but is not a directory: '" +
addonStorageBasePath.utf8Str() + "'");
}
} else {
simgear::Dir(addonStorageBasePath).create(0777); // respect user's umask
}
}
// Read in configuration (file and command line)
int fgInitConfig ( int argc, char **argv, bool reinit )
{
@ -497,7 +512,11 @@ int fgInitConfig ( int argc, char **argv, bool reinit )
if (!exportDir.exists()) {
exportDir.create(0755);
}
// Reserve a directory where add-ons can write. There will be a subdir for
// each add-on, see Addon::createStorageDir() and Addon::getStoragePath().
createBaseStorageDirForAddons(exportDir.path());
// Set /sim/fg-home. Use FG_HOME if necessary.
// deliberately not a tied property, for fgValidatePath security
// write-protect to avoid accidents