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:
parent
10caef48b1
commit
9a044a474b
5 changed files with 80 additions and 1 deletions
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue