When a partial update of tizenfx occurs, native image files of application created with FNV option must be regenerated.
However, it is not possible with the current implementation to replace the native-image files of the app installed in the read-only area like the preload app.
To solve this problem, add a functionality that creates new native images under /opt/usr/dotnet/apps, when creating a new native image files of read-only app.
SET(EXTRA_CFLAGS_COMMON "${EXTRA_CFLAGS_COMMON} -DDOTNET_DIR=${DOTNET_DIR}")
ENDIF(DEFINED DOTNET_DIR)
+IF(DEFINED READ_ONLY_APP_UPDATE_DIR)
+ SET(EXTRA_CFLAGS_COMMON "${EXTRA_CFLAGS_COMMON} -DREAD_ONLY_APP_UPDATE_DIR=${READ_ONLY_APP_UPDATE_DIR}")
+ENDIF(DEFINED READ_ONLY_APP_UPDATE_DIR)
+
IF(DEFINED USE_DEFAULT_BASE_ADDR)
SET(EXTRA_CFLAGS_COMMON "${EXTRA_CFLAGS_COMMON} -DUSE_DEFAULT_BASE_ADDR")
ENDIF(DEFINED USE_DEFAULT_BASE_ADDR)
#define NI_FLAGS_APPNI 0x0002
#define NI_FLAGS_COMPATIBILITY 0x0004
#define NI_FLAGS_VERBOSE 0x0008
+#define NI_FLAGS_READONLY_APP 0x0010
#define NI_FLAGS_INSTRUMENT 0x1000
typedef std::function<void (std::string)> afterCreate;
/**
* @brief create native images for platform DLLs (.NETCore + TizenFX)
- * @param[i] flags additional flags for the image generator
+ * @param[in] flags additional flags for the image generator
* @return ni_error_e
*/
ni_error_e createNIPlatform(DWORD flags);
/**
* @brief create a native image for a single DLL
- * @param[i] dllPath path to input DLL
- * @param[i] flags additional flags for the image generator
+ * @param[in] dllPath path to input DLL
+ * @param[in] flags additional flags for the image generator
* @return ni_error_e
*/
ni_error_e createNIDll(const std::string& dllPath, DWORD flags);
/**
* @brief create native images for all DLLs under directories
- * @param[i] rootPaths paths to directories
- * @param[i] flags additional flags for the image generator
+ * @param[in] rootPaths paths to directories
+ * @param[in] flags additional flags for the image generator
* @return ni_error_e
*/
ni_error_e createNIUnderDirs(const std::string& rootPaths, DWORD flags);
/**
* @brief create native images for all DLLs in a package
- * @param[i] pkgId package ID
- * @param[i] flags additional flags for the image generator
+ * @param[in] pkgId package ID
+ * @param[in] flags additional flags for the image generator
* @return ni_error_e
*/
ni_error_e createNIUnderPkgRoot(const std::string& pkgId, DWORD flags);
/**
* @brief remove native images under directories
- * @param[i] rootPaths paths to directories
+ * @param[in] rootPaths paths to directories
*/
void removeNIUnderDirs(const std::string& rootPaths);
/**
* @brief remove native images of a package
- * @param[i] pkgId package ID
+ * @param[in] pkgId package ID
* @return ni_error_e
*/
ni_error_e removeNIUnderPkgRoot(const std::string& pkgId);
/**
* @brief regenerate native images of all installed applications
- * @param[i] flags additional flags for the image generator
+ * @param[in] flags additional flags for the image generator
* @return ni_error_e
*/
ni_error_e regenerateAppNI(DWORD flags);
/**
* @brief regenerate native image of TAC for all shared assembly.
- * @param[i] flags additional flags for the image generator
+ * @param[in] flags additional flags for the image generator
* @return ni_error_e
*/
ni_error_e regenerateTACNI(DWORD flags);
/**
* @brief Add platform assemblies paths. The TPA(Trusted-Platform-Assembly) is generated based on this paths
- * @param[i] paths the paths to be added
- * @param[i] isHighPriority if true, paths are added in front of the current list, otherwise added at the end of the list
+ * @param[in] paths the paths to be added
+ * @param[in] isHighPriority if true, paths are added in front of the current list, otherwise added at the end of the list
*/
void addPlatformAssembliesPaths(const std::string& paths, bool isHighPriority = false);
* @brief Set application root path.
* All application related paths ("bin", "lib", ".tac_symlink", ".native_image") are generated based on it.
* A temporary path (/proc/self/fd/[fd]) is used if this function is never called.
- * @param[i] rootPath application root path
+ * @param[in] rootPath application root path
*/
void setAppRootPath(const std::string& rootPath);
/**
* @brief Get runtime path which contains coreclr and corefx
- * @return[i] runtime path
+ * @return runtime path
*/
const std::string& getRuntimePath();
/**
* @brief Get tizenfx path which contains tizenfx
- * @return[i] runtime path
+ * @return runtime path
*/
const std::string& getTizenFXPath();
/**
* @brief Get platform assemblies paths
- * @return[i] return path vector
+ * @return return path vector
*/
const std::vector<std::string>& getPlatformAssembliesPaths();
/**
* @brief Get application root path
* @see setAppRootPath()
- * @return[i] system paths
+ * @return system paths
*/
const std::string& getAppRootPath();
/**
* @brief Get the path of .tac_symlink of application
- * @return[i] .tac_symlink path
+ * @return .tac_symlink path
*/
const std::string& getAppTacPath();
/**
* @brief Get the list of directories where the assemlies of this application exist
- * @return[i] the list(":" seperated) of paths to probe in for an assembly
+ * @return the list(":" seperated) of paths to probe in for an assembly
*/
const std::string& getAppPaths();
/**
* @brief Get the list of directories where the native image of this application exist
- * @return[i] the list(":" seperated) of paths to probe in for an native image
+ * @return the list(":" seperated) of paths to probe in for an native image
*/
const std::string& getAppNIPaths();
/**
* @brief Get the list of directories where the native libraries of this application exist
- * @return[i] the list(":" seperated) of paths the loader should probe when looking for native libraries
+ * @return the list(":" seperated) of paths the loader should probe when looking for native libraries
*/
const std::string& getNativeDllSearchingPaths();
private:
- // update application related path (bin, lib, tac_symlink, native_image)
- void updateAppRelatedPath(const std::string& appRootPath);
+ /**
+ * @brief Update application related path (bin, lib, tac_symlink, native_image)
+ * In most cases, appRootPath and appNIRootPath are the same.
+ * Apps installed in read-only storage may have a different appNIRootPath.
+ * @param[in] root path of application. (APP_PATH is geneated based on root path)
+ * @param[in] root path for native image (APP_NI_PATH is generated based on on ni root path )
+ */
+ void updateAppRelatedPath(const std::string& appRootPath, const std::string& appNIRootPath);
private:
std::vector<std::string> platformAssembliesPaths;
std::string systemPaths;
std::string appRootPath;
+ std::string appNIRootPath;
std::string runtimePath;
std::string tizenfxPath;
std::string appPaths;
std::string nativeDllSearchingPaths;
std::string appTacPath;
int rootFD;
+ int niRootFD;
};
#endif /* __DLL_PATH_MANAGER_H__ */
/**
* @brief disable tac feature.
- * @param[i] pkgId package ID
+ * @param[in] pkgId package ID
* @return tac_error_e
*/
tac_error_e disableTACPackage(const std::string& pkgId);
/**
* @brief enable tac feature.
- * @param[i] pkgId package ID
+ * @param[in] pkgId package ID
* @return tac_error_e
*/
tac_error_e enableTACPackage(const std::string& pkgId);
* @brief concat path with PATH_SEPARATOR
* @param[in] destination path
* @param[in] source path
- * return std::string result path
+ * @return std::string result path
*/
std::string concatPath(const std::string& path1, const std::string& path2);
/**
* @brief get canonicalized absolute Path
* @param[in] source path
- * return std::string result path
+ * @return std::string result path
*/
std::string getAbsolutePath(const std::string& path);
/**
* @brief get the directory of file
* @param[in] source path
- * return std::string result path
+ * @return std::string result path
*/
std::string getBaseName(const std::string& path);
* @param[in] original string
* @param[in] pattern to match
* @param[in] replacement string
- * return std::string the modified string
+ * @return std::string the modified string
*/
std::string replaceAll(const std::string& str, const std::string& pattern, const std::string& replace);
/**
* @brief get root path
* @param[in] package id
- * return std::string root path
+ * @return std::string root path
*/
std::string getRootPath(const std::string& pkgId);
/**
* @brief get exec name
* @param[in] package id
- * return std::string exec name
+ * @return std::string exec name
*/
std::string getExecName(const std::string& pkgId);
/**
* @brief get app type
* @param[in] package id
- * return std::string app type
+ * @return std::string app type
*/
std::string getAppType(const std::string& pkgId);
* @brief get metadata value
* @param[in] package id
* @param[in] metadata key
- * return std::string metadata value
+ * @return std::string metadata value
*/
std::string getMetadataValue(const std::string& pkgId, const std::string& key);
/**
+ * @brief check the package is 'readonly' or not
+ * @param[in] package id
+ * @return bool package readonly value
+ */
+bool isReadOnlyApp(const std::string& pkgId);
+
+/**
* @brief split path with ":" delimiter and put that in the vector
* @param[in] source path
* @param[out] string vector
" --ni-reset-pkg - Remove App NI files\n"
" --ni-reset-dir - Remove NI for directory\n"
" --ni-regen-all-app - Re-generate All App NI files\n"
+ " --ni-regen-all-ro-app - Re-generate All read-only type App NI files\n"
" --tac-regen-all - Re-generate All TAC files\n"
" --tac-restore-db - Restore TAC Database\n"
" --tac-disable-pkg - Disable TAC for package\n"
fprintf(stderr, "Failed to regenerate all app NI\n");
}
}
+ //sh-3.2# dotnettool --ni-regen-readonly-app
+ else if (cmd == "--ni-regen-all-ro-app") {
+ flags |= NI_FLAGS_READONLY_APP;
+ int ret = regenerateAppNI(flags);
+ if (ret != NI_ERROR_NONE) {
+ fprintf(stderr, "Failed to regenerate read-only app NI\n");
+ }
+ }
//sh-3.2# dotnettool --tac-regen-all
else if (cmd == "--tac-regen-all") {
int ret = regenerateTACNI(flags);
#include <string.h>
#include <sqlite3.h>
#include <inttypes.h>
+#include <errno.h>
#include "ni_common.h"
#include "db_manager.h"
#endif
static const char* __CROSSGEN_PATH = __STR(CROSSGEN_PATH);
static const char* __DOTNET_DIR = __STR(DOTNET_DIR);
+static const char* __READ_ONLY_APP_UPDATE_DIR = __STR(READ_ONLY_APP_UPDATE_DIR);
#ifdef UNIQUE_DEFAULT_BASE_ADDR_SUPPORT
static const char* __SYSTEM_BASE_FILE = __STR(SYSTEM_BASE_FILE);
return niPath;
}
-static std::string getAppNIFilePath(const std::string& niPath)
+/**
+ * @brief create the directory including parents directory, and
+ * copy ownership and smack labels to the created directory.
+ * @param[in] target directory path
+ * @param[in] source directory path to get ownership and smack label
+ * @return if directory created successfully, return true otherwise false
+ */
+static bool createDirsAndCopyOwnerShip(std::string& target_path, const std::string& source)
+{
+ struct stat st;
+ mode_t mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
+
+ for (std::string::iterator iter = target_path.begin(); iter != target_path.end();) {
+ std::string::iterator newIter = std::find(iter, target_path.end(), '/');
+ std::string newPath = std::string(target_path.begin(), newIter);
+
+ if (!newPath.empty()) {
+ if (stat(newPath.c_str(), &st) != 0) {
+ if (mkdir(newPath.c_str(), mode) != 0 && errno != EEXIST) {
+ fprintf(stderr, "Fail to create app ni directory (%s)\n", newPath.c_str());
+ return false;
+ }
+ if (!source.empty()) {
+ copySmackAndOwnership(source, newPath);
+ }
+ } else {
+ if (!S_ISDIR(st.st_mode)) {
+ fprintf(stderr, "Fail. path is not a dir (%s)\n", newPath.c_str());
+ return false;
+ }
+ }
+ }
+ iter = newIter;
+ if(newIter != target_path.end()) {
+ ++iter;
+ }
+ }
+
+ return true;
+}
+
+static std::string getAppNIFilePath(const std::string& absDllPath, DWORD flags)
{
- std::string fileName;
std::string niDirPath;
std::string prevPath;
- size_t index = niPath.find_last_of("/");
- if (index != std::string::npos) {
- prevPath = niPath.substr(0, index);
- fileName = niPath.substr(index + 1, niPath.length());
- } else {
- prevPath = ".";
- fileName = niPath;
- }
-
+ prevPath = getBaseName(absDllPath);
niDirPath = concatPath(prevPath, APP_NI_SUB_DIR);
- if (!isFile(niDirPath)) {
- if (mkdir(niDirPath.c_str(), 0755) == 0) {
- copySmackAndOwnership(prevPath, niDirPath);
- } else {
- fprintf(stderr, "Fail to create app ni directory (%s)\n", niDirPath.c_str());
+ if (flags & NI_FLAGS_READONLY_APP) {
+ niDirPath = replaceAll(niDirPath, getBaseName(__pm->getAppRootPath()), __READ_ONLY_APP_UPDATE_DIR);
+ }
+
+ if (!isDirectory(niDirPath)) {
+ if (!createDirsAndCopyOwnerShip(niDirPath, prevPath)) {
+ niDirPath = prevPath;
+ fprintf(stderr, "fail to create dir (%s)\n", niDirPath.c_str());
}
}
- return concatPath(niDirPath, fileName);
+ return getNIFilePath(concatPath(niDirPath, getFileName(absDllPath)));
}
static bool checkNIExistence(const std::string& path)
}
std::string absDllPath = getAbsolutePath(dllPath);
- std::string absNIPath = getNIFilePath(dllPath);
+ std::string absNIPath;
+
+ bool isAppNI = flags & NI_FLAGS_APPNI;
+ if (isAppNI && strstr(absDllPath.c_str(), __DOTNET_DIR) == NULL) {
+ absNIPath = getAppNIFilePath(absDllPath, flags);
+ } else {
+ absNIPath = getNIFilePath(absDllPath);
+ }
if (absNIPath.empty()) {
fprintf(stderr, "Fail to get ni file name\n");
return NI_ERROR_UNKNOWN;
}
- bool isAppNI = flags & NI_FLAGS_APPNI;
- if (isAppNI && strstr(absNIPath.c_str(), __DOTNET_DIR) == NULL) {
- absNIPath = getAppNIFilePath(absNIPath);
- }
-
#ifdef UNIQUE_DEFAULT_BASE_ADDR_SUPPORT
uintptr_t baseAddr = 0;
return -1;
}
- if (removeNIUnderPkgRoot(pkgId) != NI_ERROR_NONE) {
- fprintf(stderr, "Failed to remove previous dlls from [%s]\n", pkgId);
- return -1;
+ bool readOnlyApp = isReadOnlyApp(pkgId);
+
+ // read-only and readonly flag set
+ if (readOnlyApp && (*pFlags & NI_FLAGS_READONLY_APP)) {
+ fprintf(stderr, "try to regenerate read-only pkg [%s]\n", pkgId);
+ }
+ // not rad-only and readonly flag doesnot set
+ else if (!readOnlyApp && !(*pFlags & NI_FLAGS_READONLY_APP)) {
+ if (removeNIUnderPkgRoot(pkgId) != NI_ERROR_NONE) {
+ fprintf(stderr, "Failed to remove previous dlls from [%s]\n", pkgId);
+ return -1;
+ }
+ }
+ // skip regeneration
+ else {
+ fprintf(stderr, "skip regeneration. pkg-type(read-only) doesnot match the configuration [%s]\n", pkgId);
+ return 0;
}
if (createNIUnderPkgRoot(pkgId, *pFlags) != NI_ERROR_NONE) {
#define __XSTR(x) #x
#define __STR(x) __XSTR(x)
static const char* __DOTNET_DIR = __STR(DOTNET_DIR);
+static const char* __READ_ONLY_APP_UPDATE_DIR = __STR(READ_ONLY_APP_UPDATE_DIR);
#undef __STR
#undef __XSTR
std::vector<std::string> removeNuget;
try {
for (auto& nuget : bf::recursive_directory_iterator(__DOTNET_DIR)) {
- bool isExist = false;
std::string nugetPath = nuget.path().string();
+ if (!bf::is_directory(nugetPath) ||
+ nugetPath.find(TLC_LIBRARIES_DIR) != std::string::npos ||
+ nugetPath.find(__READ_ONLY_APP_UPDATE_DIR) != std::string::npos) {
+ continue;
+ }
+
+ bool isExist = false;
for (auto& restore : restore_nuget) {
- if (!bf::is_directory(nugetPath) || nugetPath.find(TLC_LIBRARIES_DIR) != std::string::npos) {
- isExist = true;
- break;
- }
- if (!strcmp(nugetPath.c_str(), restore.c_str()) ||
- !strcmp(nugetPath.c_str(), restore.substr(0, restore.rfind('/')).c_str())) {
+ if (nugetPath == restore || nugetPath == getBaseName(restore)) {
isExist = true;
break;
}
static const char* __DEVICE_API_DIR = __STR(DEVICE_API_DIR);
static const char* __RUNTIME_DIR = __STR(RUNTIME_DIR);
static const char* __NATIVE_LIB_DIR = __STR(NATIVE_LIB_DIR);
+static const char* __READ_ONLY_APP_UPDATE_DIR = __STR(READ_ONLY_APP_UPDATE_DIR);
+
#undef __STR
#undef __XSTR
return candidate;
}
-void PathManager::updateAppRelatedPath(const std::string& appRootPath)
+void PathManager::updateAppRelatedPath(const std::string& appRootPath, const std::string& appNIRootPath)
{
std::string appBinPath = concatPath(appRootPath, "bin");
std::string appLibPath = concatPath(appRootPath, "lib");
+ std::string appNIBinPath = concatPath(concatPath(appNIRootPath, "bin"), APP_NI_SUB_DIR);
+ std::string appNILibPath = concatPath(concatPath(appNIRootPath, "lib"), APP_NI_SUB_DIR);
+
appTacPath = concatPath(appBinPath, TAC_SYMLINK_SUB_DIR);
appPaths = appRootPath + ":" + appBinPath + ":" + appLibPath + ":" + appTacPath;
- appNIPaths = concatPath(appBinPath, APP_NI_SUB_DIR) + ":" + concatPath(appLibPath, APP_NI_SUB_DIR) + ":"+ appTacPath;
- nativeDllSearchingPaths = runtimePath + ":" + __NATIVE_LIB_DIR + ":" + appBinPath + ":" + appLibPath + ":" + getExtraNativeLibDirs(appRootPath);
+ appNIPaths = appNIBinPath + ":" + appNILibPath + ":"+ appTacPath;
+ nativeDllSearchingPaths = runtimePath + ":" + __NATIVE_LIB_DIR + ":" + appBinPath + ":" + appLibPath + ":" + getExtraNativeLibDirs(appRootPath);
}
PathManager::PathManager() :
- rootFD(-1)
+ rootFD(-1),
+ niRootFD(-1)
{
// set runtime path
runtimePath = getAbsolutePath(__RUNTIME_DIR);
throw std::ios_base::failure("Fail to open /proc/self");
}
+ // set temporal application root path for native image
+ niRootFD = open("/proc/self", O_DIRECTORY);
+ if (niRootFD < 0) {
+ _ERR("Failed to open /proc/self");
+ throw std::ios_base::failure("Fail to open /proc/self");
+ }
+
appRootPath = std::string("/proc/self/fd/") + std::to_string(rootFD);
- updateAppRelatedPath(appRootPath);
+ appNIRootPath = std::string("/proc/self/fd/") + std::to_string(niRootFD);
+ updateAppRelatedPath(appRootPath, appNIRootPath);
_INFO("Path manager created successfully");
}
void PathManager::setAppRootPath(const std::string& rootPath)
{
+ appRootPath = getAbsolutePath(rootPath);
+
+ // check readonly update directory eixst or not
+ std::string niRootPath = replaceAll(appRootPath, getBaseName(appRootPath), __READ_ONLY_APP_UPDATE_DIR);
+ if (isDirectory(niRootPath)) {
+ appNIRootPath = getAbsolutePath(niRootPath);
+ } else {
+ appNIRootPath = appRootPath;
+ }
+
// override root path for application launch mode (candidate / standalone mode)
if (rootFD >= 0) {
- int tmpFD = open(rootPath.c_str(), O_DIRECTORY);
+ int tmpFD = open(appRootPath.c_str(), O_DIRECTORY);
dup3(tmpFD, rootFD, O_CLOEXEC);
if (tmpFD >= 0)
close(tmpFD);
}
- appRootPath = getAbsolutePath(rootPath);
- updateAppRelatedPath(appRootPath);
+ // override ni root path
+ if (niRootFD >= 0) {
+ int tmpFD = open(appNIRootPath.c_str(), O_DIRECTORY);
+ dup3(tmpFD, niRootFD, O_CLOEXEC);
+ if (tmpFD >= 0)
+ close(tmpFD);
+ }
+
+ updateAppRelatedPath(appRootPath, appNIRootPath);
}
const std::string& PathManager::getRuntimePath()
return metadataValue;
}
+bool isReadOnlyApp(const std::string& pkgId)
+{
+ bool readOnly = false;
+ int ret = 0;
+ uid_t uid = 0;
+
+ if (pkgmgr_installer_info_get_target_uid(&uid) < 0) {
+ _ERR("Failed to get UID");
+ return readOnly;
+ }
+
+ pkgmgrinfo_pkginfo_h handle;
+ if (uid == 0) {
+ ret = pkgmgrinfo_pkginfo_get_pkginfo(pkgId.c_str(), &handle);
+ } else {
+ ret = pkgmgrinfo_pkginfo_get_usr_pkginfo(pkgId.c_str(), uid, &handle);
+ }
+
+ if (ret != PMINFO_R_OK) {
+ return readOnly;
+ }
+
+ ret = pkgmgrinfo_pkginfo_is_readonly(handle, &readOnly);
+ if (ret != PMINFO_R_OK) {
+ pkgmgrinfo_pkginfo_destroy_pkginfo(handle);
+ return readOnly;
+ }
+
+ pkgmgrinfo_pkginfo_destroy_pkginfo(handle);
+
+ return readOnly;
+}
+
std::string getBaseName(const std::string& path)
{
auto pos = path.find_last_of(PATH_SEPARATOR);
</request>
<assign>
<filesystem path="/opt/usr/dotnet" label="System::Shared" type="transmutable" />
+ <filesystem path="/opt/usr/dotnet/apps" label="User::Home"/>
<filesystem path="/usr/bin/dotnet-loader" label="User" exec_label="User" />
<filesystem path="/usr/bin/dotnet" label="System::Tools" exec_label="User" />
</assign>
%define _install_plugin_dir /etc/package-manager/parserlib
%define _native_lib_dir /usr/share/dotnet.tizen/lib
%define _dotnet_dir /opt/usr/dotnet
+%define _readonly_app_update_dir /opt/usr/dotnet/apps
%define _system_base_addr_file /opt/usr/dotnet.system.base.addr
%define _ibc_data_dir /usr/share/dotnet.tizen/ibcdata
-DINSTALL_MDPLUGIN_DIR=%{_install_mdplugin_dir} \
-DINSTALL_PLUGIN_DIR=%{_install_plugin_dir} \
-DDOTNET_DIR=%{_dotnet_dir} \
+ -DREAD_ONLY_APP_UPDATE_DIR=%{_readonly_app_update_dir} \
-DVERSION=%{version} \
-DNATIVE_LIB_DIR=%{_native_lib_dir} \
%ifarch %{arm} aarch64
mv Managed/Tizen.Runtime/bin/Release/Tizen.Runtime.pdb %{buildroot}%{_framework_dir}
mkdir -p %{buildroot}%{_dotnet_dir}
+mkdir -p %{buildroot}%{_readonly_app_update_dir}
mkdir -p %{buildroot}%{_native_lib_dir}
ln -sf %{_libdir}/libsqlite3.so.0 %{buildroot}%{_native_lib_dir}/libsqlite3.so
/usr/share/parser-plugins/dotnet-launcher.info
%{_framework_dir}/Tizen.Runtime.dll
%{_dotnet_dir}
+%{_readonly_app_update_dir}
%{_ibc_data_dir}
%{_tizen_preload_dir}
%{_rw_update_scripts_dir}/%{_rw_dotnet_update_script}