From a6d1c6d9712362d15543ddfd3fd6a75ad6218d9f Mon Sep 17 00:00:00 2001 From: Arkadiusz Pietraszek Date: Wed, 18 Apr 2018 09:41:56 +0200 Subject: [PATCH 01/16] [Filesystem] Added new FileMode option and two methods. Methods added: StringToArray ArrayToString ACR: http://suprem.sec.samsung.net/jira/browse/TWDAPI-121 Change-Id: If643ebef22b43fdbd61d21f77006a5ab0cf0f04a Signed-off-by: Szymon Jastrzebski Signed-off-by: Arkadiusz Pietraszek Signed-off-by: Jakub Skowron Signed-off-by: Pawel Wasowski --- src/filesystem/js/common.js | 108 +++++++++++++++++++++++++------------------- 1 file changed, 62 insertions(+), 46 deletions(-) diff --git a/src/filesystem/js/common.js b/src/filesystem/js/common.js index adf52c7..c473ba5 100644 --- a/src/filesystem/js/common.js +++ b/src/filesystem/js/common.js @@ -22,17 +22,39 @@ var validator_ = privUtils_.validator; var types_ = validator_.Types; var native_ = new xwalk.utils.NativeManager(extension); +/* + * Create new array-like object of numbers: UTF-16 char codes from string. + * As type pass Array, Uint8Array, etc. + * Useful for passing data through crosswalk. + */ +function StringToArray(str, type) { + var len = str.length; + var output = new type(len); + for (var i = 0; i < len; i++) { + output[i] = str.charCodeAt(i); + } + return output; +} + +/* + * Pass array-like object of numbers (Array, Uint8Array, etc.), returns string. + * Each char has codepoint equal to value from array cropped with & 0xFF + * Useful for passing data through crosswalk. + */ +function ArrayToString(data) { + var output = ''; + var len = data.length; + for (var i = 0; i < len; i++) { + output += String.fromCharCode(data[i] & 0xFF); // conversion to octet + } + return output; +} + function SetReadOnlyProperty(obj, n, v) { - Object.defineProperty(obj, n, { - value: v, - writable: false - }); + Object.defineProperty(obj, n, {value: v, writable: false}); } -var FileSystemStorageType = { - INTERNAL: 'INTERNAL', - EXTERNAL: 'EXTERNAL' -}; +var FileSystemStorageType = {INTERNAL: 'INTERNAL', EXTERNAL: 'EXTERNAL'}; var FileSystemStorageState = { MOUNTED: 'MOUNTED', @@ -40,17 +62,15 @@ var FileSystemStorageState = { UNMOUNTABLE: 'UNMOUNTABLE' }; -var FileMode = { - r: 'r', - rw: 'rw', - w: 'w', - a: 'a' -}; +var FileMode = {a: 'a', r: 'r', rw: 'rw', rwo: 'rwo', w: 'w'}; + +var BaseSeekPosition = {BEGIN: 'BEGIN', CURRENT: 'CURRENT', END: 'END'}; -var tizen24home = "/opt/usr/media"; +var tizen24home = '/opt/usr/media'; -//this variable need to match same variable in common/filesystem/filesystem_provider_storage.cc -var kVirtualRootImages = "images"; +// this variable need to match same variable in +// common/filesystem/filesystem_provider_storage.cc +var kVirtualRootImages = 'images'; var commonFS_ = (function() { var cacheReady = false; @@ -60,7 +80,7 @@ var commonFS_ = (function() { var uriPrefix = 'file://'; // special condition for previous versions paths // (global paths usage issue workaround) - var isAppForEarlierVersion = privUtils_.isAppVersionEarlierThan("3.0"); + var isAppForEarlierVersion = privUtils_.isAppVersionEarlierThan('3.0'); var homeDir = undefined; function clearCache() { @@ -77,10 +97,10 @@ var commonFS_ = (function() { } var imagesPath = cacheVirtualToReal[kVirtualRootImages].path; - if (imagesPath[imagesPath.length-1] === "/") { - homeDir = imagesPath.split("/").slice(0, -2).join("/"); + if (imagesPath[imagesPath.length - 1] === '/') { + homeDir = imagesPath.split('/').slice(0, -2).join('/'); } else { - homeDir = imagesPath.split("/").slice(0, -1).join("/"); + homeDir = imagesPath.split('/').slice(0, -1).join('/'); } } @@ -130,8 +150,9 @@ var commonFS_ = (function() { }); listenerRegistered = true; } catch (e) { - privUtils_.log('Failed to register storage change listener, ' - + 'storage information may be corrupted: ' + e.message); + privUtils_.log( + 'Failed to register storage change listener, ' + + 'storage information may be corrupted: ' + e.message); } } @@ -144,8 +165,8 @@ var commonFS_ = (function() { } function removeDotsFromPath(str) { - if(str === undefined){ - return str; + if (str === undefined) { + return str; } var _pathTokens = str.split('/'); @@ -153,14 +174,15 @@ var commonFS_ = (function() { var _fileRealPath = _pathTokens[0]; _correctDir.push(_pathTokens[0]); for (var i = 1; i < _pathTokens.length; ++i) { - if(_pathTokens[i] == "..") { + if (_pathTokens[i] == '..') { if (_fileRealPath == '') { _fileRealPath = undefined; break; } var _lastDir = _correctDir.pop(); - _fileRealPath = _fileRealPath.substring(0, _fileRealPath.length - _lastDir.length - 1); - } else if(_pathTokens[i] != "."){ + _fileRealPath = + _fileRealPath.substring(0, _fileRealPath.length - _lastDir.length - 1); + } else if (_pathTokens[i] != '.') { _fileRealPath += '/' + _pathTokens[i]; _correctDir.push(_pathTokens[i]); } @@ -189,7 +211,7 @@ var commonFS_ = (function() { function convertForEarlierVersionPath(aPath) { if (isAppForEarlierVersion) { if (aPath && aPath.indexOf(tizen24home) === 0) { - privUtils_.log("Converting 2.4 style path to 3.0 pattern"); + privUtils_.log('Converting 2.4 style path to 3.0 pattern'); aPath = homeDir + aPath.substr(tizen24home.length); } } @@ -231,8 +253,9 @@ var commonFS_ = (function() { } else { _fileRealPath = aPath; } - // this line makes that '.' and '..' is supported in paths, but each method handle those cases - // and return error (see commonFS_.checkPathWithoutDots() method) + // removeDotsFromPath execution here, results with '.' and '..' beeing supported in + // paths, next methods throw an error when getting argument with '.' or '..' in it + // (see commonFS_.checkPathWithoutDots() method) _fileRealPath = removeDotsFromPath(_fileRealPath); // convert path to be compatibile with previous version of Tizen // (global paths usage issue workaround) @@ -304,7 +327,7 @@ var commonFS_ = (function() { _result.parent = (secondIter) ? null : _fileParentPath; } else { // '/' dir case - _result.path = _pathTokens[last] + lastToken;; + _result.path = _pathTokens[last] + lastToken; _result.name = ''; _result.parent = (secondIter) ? null : _fileParentPath; } @@ -332,7 +355,7 @@ var commonFS_ = (function() { } function toCanonicalPath(path) { - var result = native_.callSync('FileSystemManager_getCanonicalPath', { "path": path}); + var result = native_.callSync('FileSystemManager_getCanonicalPath', {'path': path}); if (native_.isFailure(result)) { throw native_.getErrorObject(result); } @@ -352,22 +375,15 @@ var commonFS_ = (function() { } function f_isCorrectRelativePath(relativePath) { - return ((0 !== relativePath.indexOf('/')) - && (0 !== relativePath.indexOf('\\')) - && (-1 === relativePath.indexOf('?')) - && (-1 === relativePath.indexOf('*')) - && (-1 === relativePath.indexOf(':')) - && (-1 === relativePath.indexOf('"')) - && (-1 === relativePath.indexOf('<')) && (-1 === relativePath - .indexOf('>'))); + return ( + (0 !== relativePath.indexOf('/')) && (0 !== relativePath.indexOf('\\')) && + (-1 === relativePath.indexOf('?')) && (-1 === relativePath.indexOf('*')) && + (-1 === relativePath.indexOf(':')) && (-1 === relativePath.indexOf('"')) && + (-1 === relativePath.indexOf('<')) && (-1 === relativePath.indexOf('>'))); }; function cloneStorage(storage) { - return { - label: storage.label, - type: storage.type, - state: storage.state - }; + return {label: storage.label, type: storage.type, state: storage.state}; } function getStorage(label) { -- 2.7.4 From 0dd10e4742685ad3b55090ac8b9505b5c6b2f3ce Mon Sep 17 00:00:00 2001 From: Piotr Kosko Date: Tue, 8 May 2018 07:53:49 +0200 Subject: [PATCH 02/16] [Filesystem][Alarm] Fix coverity/SVACE issues [Bug] Fixed Coverity issues: 119865, 119863. Fixed SVACE issue: 345864 Issues were related to not reachable code (invalid returned value was checked) in Alarm module and not checking returned values in Filesystem module. SVACE issue 345864 is related to the same problem as Coverity 119863. [Verification] Code compiles without errors. TCT passrate for Alarm and Filesystem - 100%. Change-Id: I315d0aed94f8efef13d37a73b313ef455fb3b00e Signed-off-by: Piotr Kosko --- src/alarm/alarm_manager.cc | 2 +- src/filesystem/filesystem_manager.cc | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/alarm/alarm_manager.cc b/src/alarm/alarm_manager.cc index 1669bb6..7a27dfc 100644 --- a/src/alarm/alarm_manager.cc +++ b/src/alarm/alarm_manager.cc @@ -630,7 +630,7 @@ PlatformResult AlarmManager::GetAlarm(int id, picojson::object& obj) { } int ret_app = app_control_get_extra_data(app_control, kAlarmRelativeDelayKey, &delay_string); - if (APP_CONTROL_ERROR_NONE != ret) { + if (APP_CONTROL_ERROR_NONE != ret_app) { return LogAndCreateResult(ErrorCode::NOT_FOUND_ERR, "Failed to get data.", ("Failed to get data: %d (%s)", ret_app, get_error_message(ret_app))); } diff --git a/src/filesystem/filesystem_manager.cc b/src/filesystem/filesystem_manager.cc index ad518b0..755defb 100644 --- a/src/filesystem/filesystem_manager.cc +++ b/src/filesystem/filesystem_manager.cc @@ -327,14 +327,20 @@ void FilesystemManager::Rename(const std::string oldPath, const std::string& new ret |= chown(newPath.c_str(), fileStat.st_uid, fileStat.st_gid); if (0 != ret) { LoggerE("Error while changing ownership/permissions [%s]", GetErrorString(errno).c_str()); - remove(newPath.c_str()); + if (0 != remove(newPath.c_str())) { + LoggerW("Error during rollback [%s], some of copied files may still exist", + GetErrorString(errno).c_str()); + } error_cb(FilesystemError::Other); return; } if (0 != remove(oldPath.c_str())) { LoggerE("Error while removing file [%s]", GetErrorString(errno).c_str()); - remove(newPath.c_str()); + if (0 != remove(newPath.c_str())) { + LoggerW("Error during rollback [%s], some of copied files may still exist", + GetErrorString(errno).c_str()); + } error_cb(FilesystemError::Other); return; } -- 2.7.4 From cea28d775844711a0ff02da64f888881fdfdc730 Mon Sep 17 00:00:00 2001 From: Piotr Kosko Date: Tue, 8 May 2018 09:16:04 +0200 Subject: [PATCH 03/16] [version] 2.23 Change-Id: I2721525833d99e46d09ffa8d34ad7b3f7c2eecf2 Signed-off-by: Piotr Kosko --- packaging/webapi-plugins.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packaging/webapi-plugins.spec b/packaging/webapi-plugins.spec index c63f171..31ca304 100644 --- a/packaging/webapi-plugins.spec +++ b/packaging/webapi-plugins.spec @@ -8,7 +8,7 @@ %define crosswalk_extensions_path %{_libdir}/%{crosswalk_extensions} Name: webapi-plugins -Version: 2.22 +Version: 2.23 Release: 0 License: Apache-2.0 and BSD-3-Clause and MIT Group: Development/Libraries -- 2.7.4 From 7f593c515a02d9ef80bbb9fd654be8390efa884f Mon Sep 17 00:00:00 2001 From: Hyotaek Shim Date: Thu, 10 May 2018 10:05:47 +0900 Subject: [PATCH 04/16] Remove unused dependency to dbus-glib Change-Id: If971f217eb9e255ae0c661bf3406ea48e430ee81 Signed-off-by: Hyotaek Shim --- packaging/webapi-plugins.spec | 1 - src/messaging/DBus/MessageProxy.h | 1 - src/messaging/messaging.gyp | 1 - 3 files changed, 3 deletions(-) diff --git a/packaging/webapi-plugins.spec b/packaging/webapi-plugins.spec index d122ecb..adc7857 100644 --- a/packaging/webapi-plugins.spec +++ b/packaging/webapi-plugins.spec @@ -490,7 +490,6 @@ BuildRequires: pkgconfig(ecore-file) BuildRequires: pkgconfig(email-service) BuildRequires: pkgconfig(msg-service) BuildRequires: pkgconfig(db-util) -BuildRequires: pkgconfig(dbus-glib-1) %endif %if "%{?tizen_feature_badge_support}" == "1" || "%{?unified_build}" == "1" diff --git a/src/messaging/DBus/MessageProxy.h b/src/messaging/DBus/MessageProxy.h index 4089fcd..aeeb96b 100644 --- a/src/messaging/DBus/MessageProxy.h +++ b/src/messaging/DBus/MessageProxy.h @@ -18,7 +18,6 @@ #ifndef __TIZEN_MESSAGE_PROXY_H #define __TIZEN_MESSAGE_PROXY_H -#include #include #include #include diff --git a/src/messaging/messaging.gyp b/src/messaging/messaging.gyp index 7edb5a6..620dbb7 100644 --- a/src/messaging/messaging.gyp +++ b/src/messaging/messaging.gyp @@ -15,7 +15,6 @@ 'msg-service', 'email-service', 'dbus-1', - 'dbus-glib-1', 'capi-system-info', 'ecore', 'ecore-file', -- 2.7.4 From 5015246348ff01ce18d6c504f3b032657019c390 Mon Sep 17 00:00:00 2001 From: Lukasz Bardeli Date: Thu, 10 May 2018 08:43:35 +0200 Subject: [PATCH 05/16] [Iotcon] Prevent iotcon from call timeout after found resource In ResourceFoundCallback after some resource was found, timeout will never be called. [Verification] Code compiles without error. TCT tests passrate 100% Change-Id: I96ca592eb94e14c374b6eab99ca1956108f37154 Signed-off-by: Lukasz Bardeli --- src/iotcon/iotcon_instance.cc | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/iotcon/iotcon_instance.cc b/src/iotcon/iotcon_instance.cc index 8339a48..3eee147 100644 --- a/src/iotcon/iotcon_instance.cc +++ b/src/iotcon/iotcon_instance.cc @@ -40,8 +40,10 @@ const std::string kPrivilegeIotcon = "http://tizen.org/privilege/internet"; struct CallbackData { common::PostCallback fun; + bool was_called; CallbackData(common::PostCallback f) { fun = f; + was_called = false; } }; @@ -1305,10 +1307,16 @@ bool IotconInstance::ResourceFoundCallback(iotcon_remote_resource_h resource, io common::TizenResult ret = common::TizenSuccess(); switch (result) { case IOTCON_ERROR_NONE: + // Mark that this callback was called at leas once (some resource was found) + data->was_called = true; ret = IotconUtils::RemoteResourceToJson(resource, &v.get()); break; case IOTCON_ERROR_TIMEOUT: LoggerD("IOTCON_TIMEOUT"); + // if TIMEOUT was called after some resource was found then do not call errorCallback + if (data->was_called) { + return IOTCON_FUNC_STOP; + } default: ret = IotconUtils::ConvertIotconError(result); } -- 2.7.4 From fc0f1cd5b8e8ffd8b4c53ea3753245b775c1c665 Mon Sep 17 00:00:00 2001 From: Arkadiusz Pietraszek Date: Wed, 18 Apr 2018 09:47:53 +0200 Subject: [PATCH 06/16] [Filesystem] Removed old methods and refactoring of argument. Methods removed: string_to_array array_to_string Refactored argument _rewrite to _truncate for consistency. ACR: http://suprem.sec.samsung.net/jira/browse/TWDAPI-121 Change-Id: I6b22bc8b65bc1e967a18c60c2416bb7519eb43b1 Signed-off-by: Szymon Jastrzebski Signed-off-by: Arkadiusz Pietraszek Signed-off-by: Jakub Skowron Signed-off-by: Pawel Wasowski --- src/filesystem/filesystem_instance.cc | 24 ++--- src/filesystem/js/file_stream.js | 172 ++++++++++++---------------------- 2 files changed, 72 insertions(+), 124 deletions(-) diff --git a/src/filesystem/filesystem_instance.cc b/src/filesystem/filesystem_instance.cc index 0747aa9..537d7c2 100644 --- a/src/filesystem/filesystem_instance.cc +++ b/src/filesystem/filesystem_instance.cc @@ -237,10 +237,10 @@ static std::vector read_file(std::string path, std::size_t offset, * On failure throws std::runtime_error */ void write_file(const std::uint8_t* data, std::size_t len, std::string path, std::size_t offset, - bool rewrite) { + bool truncate) { ScopeLogger(); - FILE* file = fopen(path.c_str(), rewrite ? "w" : "r+"); + FILE* file = fopen(path.c_str(), truncate ? "w" : "r+"); if (!file) { throw std::runtime_error("cannot open file to write"); @@ -396,12 +396,12 @@ void FilesystemInstance::FileWriteString(const picojson::value& args, picojson:: CHECK_EXIST(args, "location", out) CHECK_EXIST(args, "data", out) CHECK_EXIST(args, "offset", out) - CHECK_EXIST(args, "rewrite", out) + CHECK_EXIST(args, "truncate", out) const std::string& location = args.get("location").get(); const std::string& str = args.get("data").get(); size_t offset = static_cast(args.get("offset").get()); - bool rewrite = static_cast(args.get("rewrite").get()); + bool truncate = static_cast(args.get("truncate").get()); const std::string& encoding = args.contains("encoding") ? args.get("encoding").get() : "utf-8"; @@ -409,11 +409,11 @@ void FilesystemInstance::FileWriteString(const picojson::value& args, picojson:: if (encoding == "iso-8859-1") { std::vector data; latin1::from_utf8(str, data); - write_file(data.data(), data.size(), location, offset, rewrite); + write_file(data.data(), data.size(), location, offset, truncate); } else { // default: UTF-8 const std::uint8_t* buf = (const std::uint8_t*)str.c_str(); std::size_t len = str.length(); - write_file(buf, len, location, offset, rewrite); + write_file(buf, len, location, offset, truncate); } } catch (std::runtime_error& e) { LoggerE("Cannot write to file %s, cause: %s", location.c_str(), e.what()); @@ -430,17 +430,17 @@ void FilesystemInstance::FileWriteBytes(const picojson::value& args, picojson::o CHECK_EXIST(args, "location", out) CHECK_EXIST(args, "data", out) CHECK_EXIST(args, "offset", out) - CHECK_EXIST(args, "rewrite", out) + CHECK_EXIST(args, "truncate", out) const std::string& location = args.get("location").get(); const std::string& str = args.get("data").get(); size_t offset = static_cast(args.get("offset").get()); - bool rewrite = static_cast(args.get("rewrite").get()); + bool truncate = static_cast(args.get("truncate").get()); try { std::vector data; decode_binary_from_string(str, data); - write_file(data.data(), data.size(), location, offset, rewrite); + write_file(data.data(), data.size(), location, offset, truncate); } catch (std::runtime_error& e) { LoggerE("Cannot write to %s, cause: %s", location.c_str(), e.what()); PrepareError(FilesystemError::Other, out); @@ -456,12 +456,12 @@ void FilesystemInstance::FileWriteBase64(const picojson::value& args, picojson:: CHECK_EXIST(args, "location", out) CHECK_EXIST(args, "data", out) CHECK_EXIST(args, "offset", out) - CHECK_EXIST(args, "rewrite", out) + CHECK_EXIST(args, "truncate", out) const std::string& location = args.get("location").get(); const std::string& str = args.get("data").get(); size_t offset = static_cast(args.get("offset").get()); - bool rewrite = static_cast(args.get("rewrite").get()); + bool truncate = static_cast(args.get("truncate").get()); std::vector data; try { @@ -473,7 +473,7 @@ void FilesystemInstance::FileWriteBase64(const picojson::value& args, picojson:: } try { - write_file(data.data(), data.size(), location, offset, rewrite); + write_file(data.data(), data.size(), location, offset, truncate); ReportSuccess(picojson::value{(double)data.size()}, out); } catch (std::runtime_error& e) { LoggerE("Cannot write to %s, cause: %s", location.c_str(), e.what()); diff --git a/src/filesystem/js/file_stream.js b/src/filesystem/js/file_stream.js index 92194a1..9d391bb 100644 --- a/src/filesystem/js/file_stream.js +++ b/src/filesystem/js/file_stream.js @@ -25,8 +25,7 @@ function FileStream(data, mode, encoding) { get: function() { return _totalBytes < _position; }, - set: function(v) { - }, + set: function(v) {}, enumerable: true }, position: { @@ -45,32 +44,15 @@ function FileStream(data, mode, encoding) { get: function() { return this.eof ? -1 : Math.max(0, _totalBytes - _position); }, - set: function(v) { - }, + set: function(v) {}, enumerable: true }, - _mode: { - value: mode, - writable: false, - enumerable: false - }, - _encoding: { - value: encoding, - writable: false, - enumerable: false - }, - _file: { - value: data, - writable: false, - enumerable: false - }, - _closed: { - value: false, - writable: true, - enumerable: false - }, - _rewrite: { - value: mode === 'w' ? true : false, + _mode: {value: mode, writable: false, enumerable: false}, + _encoding: {value: encoding, writable: false, enumerable: false}, + _file: {value: data, writable: false, enumerable: false}, + _closed: {value: false, writable: true, enumerable: false}, + _truncate: { + value: mode === 'w', // 'w' truncates file to zero length writable: true, enumerable: false } @@ -85,7 +67,7 @@ function _checkClosed(stream) { function closeFileStream() { this._closed = true; -}; +} FileStream.prototype.close = function() { closeFileStream.apply(this, arguments); @@ -103,51 +85,27 @@ function _checkWriteAccess(mode) { } } -/* returns array of numbers */ -function string_to_array( str ) { - var output = []; - var len = str.length; - for( var i = 0; i < len; i++ ) { - output.push( str.charCodeAt(i) ); - } - return output; -} - -/* receives array of numbers, returns string */ -function array_to_string( data ) { - var output = ""; - var len = data.length; - for( var i = 0; i < len; i++ ) { - output += String.fromCharCode(data[i] & 0xFF); // conversion to octet - } - return output; -} - function read() { - var args = validator_.validateArgs(arguments, [ - { - name: 'charCount', - type: types_.LONG - } - ]); + var args = validator_.validateArgs(arguments, [{name: 'charCount', type: types_.LONG}]); _checkClosed(this); _checkReadAccess(this._mode); if (!arguments.length) { - throw new WebAPIException(WebAPIException.INVALID_VALUES_ERR, - 'Argument "charCount" missing'); + throw new WebAPIException( + WebAPIException.INVALID_VALUES_ERR, 'Argument "charCount" missing'); } if (!type_.isNumber(args.charCount)) { - throw new WebAPIException(WebAPIException.TYPE_MISMATCH_ERR, - 'Argument "charCount" must be a number'); + throw new WebAPIException( + WebAPIException.TYPE_MISMATCH_ERR, 'Argument "charCount" must be a number'); } if (args.charCount <= 0) { - throw new WebAPIException(WebAPIException.INVALID_VALUES_ERR, + throw new WebAPIException( + WebAPIException.INVALID_VALUES_ERR, 'Argument "charCount" must be greater than 0'); } - if(this.eof) { - throw new WebAPIException(WebAPIException.IO_ERR, 'Stream is marked as EOF.'); + if (this.eof) { + throw new WebAPIException(WebAPIException.IO_ERR, 'Stream is marked as EOF.'); } var _count = this.bytesAvailable; @@ -170,29 +128,25 @@ function read() { this.position += outData.length; can_change_size = false; } else { - this.position += 1; // Set EOF + this.position += 1; // Set EOF } return outData; -}; +} FileStream.prototype.read = function() { return read.apply(this, arguments); }; function readBytes() { - var args = validator_.validateArgs(arguments, [ - { - name: 'byteCount', - type: types_.LONG - } - ]); + var args = validator_.validateArgs(arguments, [{name: 'byteCount', type: types_.LONG}]); _checkClosed(this); _checkReadAccess(this._mode); if (args.byteCount <= 0) { - throw new WebAPIException(WebAPIException.INVALID_VALUES_ERR, + throw new WebAPIException( + WebAPIException.INVALID_VALUES_ERR, 'Argument "byteCount" must be greater than 0'); } @@ -209,18 +163,18 @@ function readBytes() { throw new WebAPIException(WebAPIException.INVALID_VALUES_ERR, 'Could not read'); } - var decoded = string_to_array( native_.getResultObject(result) ); + var decoded = StringToArray(native_.getResultObject(result), Array); if (decoded.length) { can_change_size = true; this.position += decoded.length; can_change_size = false; } else { - this.position += 1; // Set EOF + this.position += 1; // Set EOF } return decoded; -}; +} FileStream.prototype.readBytes = function() { return readBytes.apply(this, arguments); @@ -228,31 +182,29 @@ FileStream.prototype.readBytes = function() { FileStream.prototype.readBase64 = function() { return base64_encode(readBytes.apply(this, arguments)); -} +}; -function check_characters_outside_latin1( str ) { +function check_characters_outside_latin1(str) { var len = str.length; - for( var i = 0; i < len; ++i ) { - if( str.charCodeAt(i) > 255 ) { - throw new WebAPIException(WebAPIException.IO_ERR, 'Invalid character at '+i+': '+str.charAt(i)+' (not ISO-8859-1)'); + for (var i = 0; i < len; ++i) { + if (str.charCodeAt(i) > 255) { + throw new WebAPIException( + WebAPIException.IO_ERR, + 'Invalid character at ' + i + ': ' + str.charAt(i) + ' (not ISO-8859-1)'); } } } function write() { - var args = validator_.validateArgs(arguments, [ - { - name: 'stringData', - type: types_.STRING - } - ]); + var args = + validator_.validateArgs(arguments, [{name: 'stringData', type: types_.STRING}]); _checkClosed(this); _checkWriteAccess(this._mode); if (!arguments.length) { - throw new WebAPIException(WebAPIException.NOT_FOUND_ERR, - 'Argument "stringData" missing'); + throw new WebAPIException( + WebAPIException.NOT_FOUND_ERR, 'Argument "stringData" missing'); } var data = { @@ -260,10 +212,10 @@ function write() { encoding: this._encoding, offset: this.position, data: args.stringData, - rewrite: this._rewrite + truncate: this._truncate }; - if( data.encoding == "iso-8859-1") { + if (data.encoding == 'iso-8859-1') { check_characters_outside_latin1(data.data); } @@ -275,35 +227,35 @@ function write() { can_change_size = true; this.position = this.position + args.stringData.length; can_change_size = false; - this._rewrite = false; -}; + this._truncate = false; +} FileStream.prototype.write = function() { write.apply(this, arguments); }; function writeBytes() { - var args = validator_.validateArgs(arguments, [ - { - name: 'byteData', - type: types_.ARRAY, - values: undefined /* was types_.OCTET, but checking moved to array_to_string for performance */ - } - ]); + var args = validator_.validateArgs( + arguments, [{ + name: 'byteData', + type: types_.ARRAY, + values: undefined /* was types_.OCTET, but checking moved to ArrayToString for + performance */ + }]); _checkClosed(this); _checkWriteAccess(this._mode); if (!arguments.length) { - throw new WebAPIException(WebAPIException.TYPE_MISMATCH_ERR, - 'Argument "byteData" missing'); + throw new WebAPIException( + WebAPIException.TYPE_MISMATCH_ERR, 'Argument "byteData" missing'); } var data = { location: commonFS_.toRealPath(this._file.fullPath), offset: this.position, - data: array_to_string(args.byteData), - rewrite: this._rewrite, + data: ArrayToString(args.byteData), + truncate: this._truncate, }; var result = native_.callSync('File_writeBytes', data); @@ -314,20 +266,16 @@ function writeBytes() { can_change_size = true; this.position = this.position + args.byteData.length; can_change_size = false; - this._rewrite = false; -}; + this._truncate = false; +} FileStream.prototype.writeBytes = function() { writeBytes.apply(this, arguments); }; function writeBase64() { - var args = validator_.validateArgs(arguments, [ - { - name: 'base64Data', - type: types_.STRING - } - ]); + var args = + validator_.validateArgs(arguments, [{name: 'base64Data', type: types_.STRING}]); _checkClosed(this); _checkWriteAccess(this._mode); @@ -336,13 +284,13 @@ function writeBase64() { location: commonFS_.toRealPath(this._file.fullPath), offset: this.position, data: args.base64Data, - rewrite: this._rewrite, + truncate: this._truncate, }; var result = native_.callSync('File_writeBase64', data); if (native_.isFailure(result)) { - throw native_.getErrorObject(result); + throw native_.getErrorObject(result); } var written_bytes = native_.getResultObject(result); @@ -350,8 +298,8 @@ function writeBase64() { can_change_size = true; this.position += written_bytes; can_change_size = false; - this._rewrite = false; -}; + this._truncate = false; +} FileStream.prototype.writeBase64 = function() { writeBase64.apply(this, arguments); -- 2.7.4 From 61143b4d7d06bdd2f22824b2c9a67359427fed8f Mon Sep 17 00:00:00 2001 From: Arkadiusz Pietraszek Date: Wed, 18 Apr 2018 11:10:04 +0200 Subject: [PATCH 07/16] [Filesystem] Added utility functions to be used in filesystem module. Functions added: Mkdir Unlink PosixBasename Dirname CopyFile CopyDirectory ListDirectory RemoveDirectoryRecursively RemoveDirectory RealPath CheckIfExists CheckIfDir CheckIfFile Rename MoveFile MoveDirectory TranslateException ACR: http://suprem.sec.samsung.net/jira/browse/TWDAPI-121 Change-Id: I7ebaa5fbacc31c941f46fd9372a827c15b69a632 Signed-off-by: Szymon Jastrzebski Signed-off-by: Arkadiusz Pietraszek Signed-off-by: Jakub Skowron Signed-off-by: Pawel Wasowski --- src/filesystem/filesystem_manager.cc | 2 +- src/filesystem/filesystem_utils.cc | 342 +++++++++++++++++++++++++++++++++-- src/filesystem/filesystem_utils.h | 120 +++++++++++- 3 files changed, 436 insertions(+), 28 deletions(-) diff --git a/src/filesystem/filesystem_manager.cc b/src/filesystem/filesystem_manager.cc index 755defb..a95f5b1 100644 --- a/src/filesystem/filesystem_manager.cc +++ b/src/filesystem/filesystem_manager.cc @@ -196,7 +196,7 @@ FilesystemError make_directory_worker(const std::string& path) { } } - std::string parent_path = FilesystemUtils::get_dirname(path); + std::string parent_path = FilesystemUtils::GetDirname(path); auto parent_result = make_directory_worker(parent_path); if (parent_result == FilesystemError::DirectoryExists) { diff --git a/src/filesystem/filesystem_utils.cc b/src/filesystem/filesystem_utils.cc index c6d04e5..3ae8574 100644 --- a/src/filesystem/filesystem_utils.cc +++ b/src/filesystem/filesystem_utils.cc @@ -14,27 +14,338 @@ * limitations under the License. */ #include "filesystem_utils.h" +#include "common/logger.h" +#include "common/platform_exception.h" +#include +#include +#include #include #include -#include "common/logger.h" +#include +#include +#include +#include namespace FilesystemUtils { -std::string get_storage_dir_path(int id, storage_directory_e typeToCheck) { +using namespace std::string_literals; +using namespace common; +using common::tools::ReportError; + +void Mkdir(const std::string& path) { + ScopeLogger("%s", path.c_str()); + int ret = ::mkdir(path.c_str(), S_IRWXU | S_IRWXG | S_IRWXO); + + if (0 != ret) { + throw std::system_error{errno, std::generic_category()}; + } +} + +void Mkdir(const std::string& path, bool parents) { + // ScopeLogger("%s, %d", path.c_str(), parents); // disabled in recursive function + if (!parents) { + Mkdir(path); + return; + } + + struct ::stat buf; + std::vector stack; + // iterate path up to first existing element + for (std::string s = path; 0 != ::stat(s.c_str(), &buf); s = Dirname(s)) { + if (ENOENT == errno) { + stack.push_back(s); + } else { + throw std::system_error{errno, std::generic_category()}; + } + } + + if (stack.empty()) { // this means, that path exists, let Mkdir handle error + Mkdir(path); + return; + } + + // from top to bottom + for (auto rit = stack.rbegin(); rit != stack.rend(); ++rit) { + try { + Mkdir(*rit); + } catch (const std::system_error& error) { + if (stack.rbegin() != rit) { + try { + RemoveDirectoryRecursively(*stack.rbegin()); + } catch (const std::system_error& removalError) { + LoggerW( + "Could not remove parent directories created so far: %s." + "Some of them might still exist", + removalError.what()); + } + } + throw; + } + } +} + +void Unlink(const std::string& path) { + ScopeLogger(); + int ret = ::unlink(path.c_str()); + if (0 != ret) { + throw std::system_error{errno, std::generic_category()}; + } +} + +std::string PosixBasename(const std::string& path) { + ScopeLogger(); + char* s = ::strdup(path.c_str()); + if (!s) { + throw std::system_error{errno, std::generic_category(), path}; + } + + // basename will never fail + std::string name{::basename(s)}; + free(s); + return name; +} + +std::string Dirname(const std::string& path) { + ScopeLogger(); + char* s = ::strdup(path.c_str()); + if (!s) { + throw std::system_error{errno, std::generic_category(), path}; + } + // dirname will never fail + std::string dir{::dirname(s)}; + free(s); + return dir; +} + +void CopyFile(const std::string& src, const std::string& dest, bool overwrite) { + ScopeLogger("From: %s; To %s", src.c_str(), dest.c_str()); + struct stat buf {}; + if (CheckIfExists(dest, &buf) && CheckIfDir(buf)) { + if (overwrite) { + RemoveDirectoryRecursively(dest); + } else { + throw std::system_error{EIO, std::generic_category(), + "Failed to copy file: overwrite is not allowed."}; + } + } + + GError* error = nullptr; + auto source_ptr = std::unique_ptr( + g_file_new_for_path(src.c_str()), g_object_unref); + auto dest_ptr = std::unique_ptr( + g_file_new_for_path(dest.c_str()), g_object_unref); + int flags = G_FILE_COPY_ALL_METADATA; + if (overwrite) { + flags |= G_FILE_COPY_OVERWRITE; + } + + gboolean success = + g_file_copy(source_ptr.get(), dest_ptr.get(), static_cast(flags), nullptr, + nullptr, nullptr, &error); + if (!success) { + std::string why{}; + if (error) { + why = error->message; + g_error_free(error); + } + throw std::system_error{EIO, std::generic_category(), "Failed to copy file: "s + why}; + } +} + +void CopyDirectory(const std::string& src, const std::string& dest, bool overwrite) { + ScopeLogger("From: %s; To %s", src.c_str(), dest.c_str()); + std::string dest_dir = dest + '/' + PosixBasename(src); + struct stat buf {}; + bool exists = CheckIfExists(dest_dir, &buf); + if (exists && !CheckIfDir(buf)) { + if (overwrite) { + Unlink(dest_dir); + Mkdir(dest_dir); + } else { + throw std::system_error{EIO, std::generic_category(), + "Failed to copy directory: overwrite is not allowed."}; + } + } else if (!exists) { + Mkdir(dest_dir); + } + + ListDirectory(src, [&](const char* name, unsigned char type) { + if (DT_DIR == type) { + CopyDirectory(src + '/' + name, dest_dir, overwrite); + } else { // copying of regular files as well as other types of items pointed by src + CopyFile(src + '/' + name, dest_dir + '/' + name, overwrite); + } + // Set errno to 0 to prevent from reporting readdir error after successful iterating through + // directory. + errno = 0; + }); +} + +void ListDirectory(const std::string& path, std::function next) { + ScopeLogger("%s", path.c_str()); + DIR* d = ::opendir(path.c_str()); + if (nullptr == d) { + throw std::system_error{errno, std::generic_category(), + "Failed to open directory: "s + std::strerror(errno)}; + } + + std::unique_ptr dir_ptr(d, [](DIR * d) { + if (::closedir(d)) { + LoggerW("closedir failed"); + } + }); + errno = 0; + + for (dirent* entry; (entry = ::readdir(d));) { + if (0 == std::strcmp(entry->d_name, ".") || 0 == std::strcmp(entry->d_name, "..")) { + continue; + } + next(entry->d_name, entry->d_type); + } + + if (0 != errno) { + throw std::system_error{errno, std::generic_category(), + "Failed to read directory: "s + std::strerror(errno)}; + } +} + +void RemoveDirectoryRecursively(const std::string& path) { + ScopeLogger("%s", path); + auto res = + nftw(path.c_str(), + [](const char* fpath, const struct stat* sb, int typeflag, struct FTW* ftwbuf) -> int { + // if number of nested directories is large + // below log could decrease readability + // ScopeLogger("%s", fpath); + + auto res = remove(fpath); + if (res) { + LoggerD("Failed to remove %s: %s", fpath, std::strerror(errno)); + return errno; + } + return 0; + }, + 128, FTW_DEPTH | FTW_PHYS); + + if (res) { + throw std::system_error{res, std::generic_category(), + "Failed to remove directory recursively: "s + std::strerror(res)}; + } +} + +void RemoveDirectory(const std::string& path) { + ScopeLogger(); + if (rmdir(path.c_str())) { + throw std::system_error{errno, std::generic_category(), "Failed to remove directory"}; + } +} + +std::string RealPath(const std::string& path) { ScopeLogger(); - char* platformPath = NULL; - int result = storage_get_directory(id, typeToCheck, &platformPath); - if (STORAGE_ERROR_NONE != result) { - LoggerD("Cannot retrieve path for type %i: %d (%s)", typeToCheck, result, - get_error_message(result)); - return std::string(); + char* real_path = realpath(path.c_str(), nullptr); + if (nullptr == real_path) { + throw std::system_error{errno, std::generic_category(), "Path is not valid."}; } - std::string path = std::string(platformPath); - free(platformPath); - return path; + std::string s{real_path}; + free(real_path); + return s; } -std::string get_dirname(const std::string& path) { +bool CheckIfExists(const std::string& path, struct stat* buf) { + ScopeLogger(); + if (stat(path.c_str(), buf)) { + if (ENOENT == errno) { + return false; + } else { + throw std::system_error{errno, std::generic_category(), + "Unable to check file existence: "s + std::strerror(errno)}; + } + } + return true; +} + +bool CheckIfDir(const struct stat& buf) { + ScopeLogger(); + if (S_ISDIR(buf.st_mode)) { + return true; + } + return false; +} + +bool CheckIfFile(const struct stat& buf) { + ScopeLogger(); + if (S_ISREG(buf.st_mode)) { + return true; + } + return false; +} + +void Rename(const std::string& path, const std::string& new_path) { + ScopeLogger(); + if (::rename(path.c_str(), new_path.c_str())) { + throw std::system_error{errno, std::generic_category(), + "Unable to rename file or directory: "s + std::strerror(errno)}; + } +} + +void MoveFile(const std::string& path, const std::string& new_path, bool overwrite) { + ScopeLogger(); + GError* error = nullptr; + auto source_ptr = std::unique_ptr( + g_file_new_for_path(path.c_str()), g_object_unref); + auto dest_ptr = std::unique_ptr( + g_file_new_for_path(new_path.c_str()), g_object_unref); + int flags = G_FILE_COPY_ALL_METADATA; + if (overwrite) { + flags |= G_FILE_COPY_OVERWRITE; + } + gboolean success = + g_file_move(source_ptr.get(), dest_ptr.get(), static_cast(flags), nullptr, + nullptr, nullptr, &error); + if (!success) { + std::string why{}; + if (error) { + why = error->message; + g_error_free(error); + } + throw std::system_error{EIO, std::generic_category(), "Failed to move file: "s + why}; + } +} + +void MoveDirectory(const std::string& src, const std::string& dest, bool overwrite) { + ScopeLogger("%s %s", src.c_str(), dest.c_str()); + struct stat buf {}; + const std::string& new_path = dest + '/' + PosixBasename(src); + // If directories are on the same mount point, we can simply try to rename them. + // However, it might be done only if new_path does not exist because move_directory should merge + // directories. + if (!CheckIfExists(new_path, &buf)) { + LoggerD("new_path %s", new_path.c_str()); + auto result = ::rename(src.c_str(), new_path.c_str()); + if (!result) { + return; + } else if (EXDEV != errno) { + // The directories are in the same mount point, but the operation has just failed. + throw std::system_error{EIO, std::generic_category(), + "Unable to move directory: "s + std::strerror(errno)}; + } + } + + // Move directory to other move point. + CopyDirectory(src, dest, overwrite); + RemoveDirectoryRecursively(src); +} + +void TranslateException(const std::system_error& e, picojson::object& obj) { + ScopeLogger(); + if (std::errc::no_such_file_or_directory == e.code()) { + LogAndReportError(NotFoundException(e.what()), obj); + } else { + LogAndReportError(IOException(e.what()), obj); + } +} + +std::string GetDirname(const std::string& path) { ScopeLogger(); char* dir = g_path_get_dirname(path.c_str()); if (dir) { @@ -45,11 +356,4 @@ std::string get_dirname(const std::string& path) { return std::string("."); } } - -std::string get_basename(const std::string& path) { - ScopeLogger(); - // basename will modify content: pass a copy - std::string buf = path.c_str(); - return std::string(basename(const_cast(buf.c_str()))); -} } diff --git a/src/filesystem/filesystem_utils.h b/src/filesystem/filesystem_utils.h index f59048e..5888284 100644 --- a/src/filesystem/filesystem_utils.h +++ b/src/filesystem/filesystem_utils.h @@ -17,9 +17,16 @@ #ifndef FILESYSTEM_FILESYSTEM_UTILS_H #define FILESYSTEM_FILESYSTEM_UTILS_H -#include -#include #include "common/picojson.h" +#include "common/tools.h" + +#include +#include +#include +#include +#include +#include +#include namespace extension { namespace filesystem { @@ -40,14 +47,111 @@ enum class FilesystemError { namespace FilesystemUtils { /** - * @brief get_storage_dir_path attempts to get path from storage. - * If path cannot be retrieved then an empty string is returned. - * + * @brief Wrapper for POSIX mkdir function. + * @throw std::system_error + */ +void Mkdir(const std::string& path); + +/** + * @brief Make directory using mkdir. If 'parents' is true, make parent directories as needed + * @throw std::system_error */ -std::string get_storage_dir_path(int id, storage_directory_e typeToCheck); +void Mkdir(const std::string& path, bool parents); + +/** + * @brief Wrapper for POSIX unlink function + * @throw std::system_error + */ +void Unlink(const std::string& path); + +/** + * @brief Returns last element of path (wrapper for POSIX basename function) + * @throw std::system_error + */ +std::string PosixBasename(const std::string& path); + +/** + * @brief Returns parent directory of path (wrapper for POSIX dirname function) + * @throw std::system_error + */ +std::string Dirname(const std::string& path); + +/** + * @brief Wrapper for GLib g_file_copy function. + * @throw std::system_error + */ +void CopyFile(const std::string& src, const std::string& dest, bool overwrite); + +/** + * @brief Copies directory recursively + * @throw std::system_error + */ +void CopyDirectory(const std::string& src, const std::string& dest, bool overwrite); + +/** + * @brief Calls 'next' function with name for every entry in given directory + * @throw std::system_error + */ +void ListDirectory(const std::string& path, std::function next); + +/** + * @brief Removes directory recursively pointed by path. + * @throw std::system_error + */ +void RemoveDirectoryRecursively(const std::string& path); + +/** + * @brief Removes directory pointed by path. + * @throw std::system_error + */ +void RemoveDirectory(const std::string& path); + +/** + * @brief Returns the real path. + * @throw std::system_error + */ +std::string RealPath(const std::string& path); + +/** + * @brief Checks if path points to file or directory. + * @throw std::system_error + */ +bool CheckIfExists(const std::string& path, struct stat* buf); + +/** + * @brief Checks if path points to directory. + * @throw std::system_error + */ +bool CheckIfDir(const struct stat& buf); + +/** + * @brief Checks if path points to file. + * @throw std::system_error + */ +bool CheckIfFile(const struct stat& buf); + +/** + * @brief Renames file or directory. + * @throw std::system_error + */ +void Rename(const std::string& path, const std::string& new_path); + +/** + * @brief Wrapper for GLib g_file_move function. + * @throw std::system_error + */ +void MoveFile(const std::string& path, const std::string& new_path, bool overwrite); + +/** + * @brief Moves directory by recursively calling move_file. + * @throw std::system_error + */ +void MoveDirectory(const std::string& path, const std::string& new_path, bool overwrite); + +void TranslateException(const std::system_error& e, picojson::object& obj); -std::string get_dirname(const std::string& path); -std::string get_basename(const std::string& path); +// This function is left only for compatibility with previous implementation in FilesystemManager +std::string GetDirname(const std::string& path); } #endif // FILESYSTEM_FILESYSTEM_UTILS_H -- 2.7.4 From c21d083235cde5a69de6665f9325d88fe2bd50d5 Mon Sep 17 00:00:00 2001 From: Arkadiusz Pietraszek Date: Thu, 19 Apr 2018 10:05:18 +0200 Subject: [PATCH 08/16] [Filesystem] Filehandle class added. Some refactoring. ACR: http://suprem.sec.samsung.net/jira/browse/TWDAPI-121 Change-Id: I3380297e0fc4f03032a9892e47392564786871bd Signed-off-by: Szymon Jastrzebski Signed-off-by: Arkadiusz Pietraszek Signed-off-by: Jakub Skowron Signed-off-by: Pawel Wasowski --- src/filesystem/filesystem_instance.cc | 127 +++++++++++++++++++++++++--------- src/filesystem/filesystem_instance.h | 31 +++++++++ 2 files changed, 124 insertions(+), 34 deletions(-) diff --git a/src/filesystem/filesystem_instance.cc b/src/filesystem/filesystem_instance.cc index 1e2f044..33d0673 100644 --- a/src/filesystem/filesystem_instance.cc +++ b/src/filesystem/filesystem_instance.cc @@ -16,11 +16,13 @@ #include "filesystem/filesystem_instance.h" +#include #include #include #include #include +#include #include "common/logger.h" #include "common/picojson.h" #include "common/platform_exception.h" @@ -40,6 +42,16 @@ const std::string kPrivilegeFilesystemWrite = "http://tizen.org/privilege/filesy using namespace common; using namespace extension::filesystem; +using namespace std::string_literals; + +FileHandle::~FileHandle() { + ScopeLogger(); + + if (file_handle && std::fclose(file_handle)) { + int errsv = errno; + LoggerE("close file failed, error message: %s", strerror(errsv)); + } +} FilesystemInstance::FilesystemInstance() { ScopeLogger(); @@ -185,17 +197,36 @@ static auto from_utf8 = &decode_binary_from_string; } static constexpr std::size_t NPOS = (std::size_t)(-1); + +/** + * On failure throws std::system_error + */ +static std::size_t file_size(FILE* file) { + ScopeLogger(); + + struct ::stat buf; + int status = ::fstat(::fileno(file), &buf); + if (status != 0) { + throw std::system_error{errno, std::generic_category(), "failed to get file size"}; + } + + return buf.st_size; +} + +static std::vector read_file(FILE* file, std::size_t length = NPOS); + /** * Returns a buffer. If length is NPOS, then it reads whole file, up to the end. * On failure throws std::runtime_error */ -static std::vector read_file(std::string path, std::size_t offset, +static std::vector read_file(std::string path, long offset = 0, std::size_t length = NPOS) { ScopeLogger(); FILE* file = std::fopen(path.c_str(), "r"); if (!file) { - throw std::runtime_error("cannot open file to read"); + std::string err_msg = std::string("cannot open file to read") + strerror(errno); + throw std::system_error{errno, std::generic_category(), err_msg}; } SCOPE_EXIT { @@ -205,17 +236,28 @@ static std::vector read_file(std::string path, std::size_t offset, } }; - if (std::fseek(file, offset, SEEK_SET) != 0) { - throw std::runtime_error("cannot perform seek"); + if (0 != offset && 0 != std::fseek(file, offset, SEEK_SET)) { + std::string err_msg = std::string("cannot perform seek") + strerror(errno); + throw std::system_error{errno, std::generic_category(), err_msg}; + } + + if (NPOS == length) { + length = file_size(file) - offset; } + return read_file(file, length); +} + +/** + * Returns a buffer. If length is NPOS, then it reads whole file, up to the end. + * On failure throws std::runtime_error + */ +static std::vector read_file(FILE* file, std::size_t length /*= NPOS*/) { + ScopeLogger(); + // By default reads whole file. Get the file size. - if (length == NPOS) { - struct ::stat buf; - if (::fstat(::fileno(file), &buf) != 0) { - throw std::runtime_error("cannot fstat"); - } - length = buf.st_size - offset; + if (NPOS == length) { + length = file_size(file); } std::vector out_buf(length); @@ -225,7 +267,8 @@ static std::vector read_file(std::string path, std::size_t offset, data_p += std::fread(data_p, 1, end_p - data_p, file); if (std::ferror(file)) { - throw std::runtime_error("error during file read"); + std::string err_msg = std::string("error during file read") + strerror(errno); + throw std::runtime_error(err_msg); } if (std::feof(file)) { @@ -239,14 +282,38 @@ static std::vector read_file(std::string path, std::size_t offset, /** * On failure throws std::runtime_error */ -void write_file(const std::uint8_t* data, std::size_t len, std::string path, std::size_t offset, - bool truncate) { +void write_file(const std::uint8_t* data, std::size_t len, FILE* file) { ScopeLogger(); - FILE* file = fopen(path.c_str(), truncate ? "w" : "r+"); + const std::uint8_t* data_p = data; + const std::uint8_t* end_p = data + len; + while (data_p != end_p) { + data_p += fwrite(data_p, 1, end_p - data_p, file); + + if (std::ferror(file)) { + std::string err_msg = std::string("error during file write") + strerror(errno); + throw std::runtime_error(err_msg); + } + } + + if (std::fflush(file)) { + std::string err_msg = std::string("error during file write") + strerror(errno); + throw std::runtime_error(err_msg); + } +} + +/** + * On failure throws std::runtime_error + */ +void write_file(const std::uint8_t* data, std::size_t len, std::string path, long offset, + const char* mode) { + ScopeLogger(); + + FILE* file = std::fopen(path.c_str(), mode); if (!file) { - throw std::runtime_error("cannot open file to write"); + std::string err_msg = std::string("cannot open file to write") + strerror(errno); + throw std::runtime_error(err_msg); } SCOPE_EXIT { @@ -256,23 +323,12 @@ void write_file(const std::uint8_t* data, std::size_t len, std::string path, std } }; - if (std::fseek(file, offset, SEEK_SET) != 0) { - throw std::runtime_error("cannot perform seek"); + if (offset != 0 && std::fseek(file, offset, SEEK_SET) != 0) { + std::string err_msg = std::string("cannot perform seek") + strerror(errno); + throw std::system_error{errno, std::generic_category(), err_msg}; } - const std::uint8_t* data_p = data; - const std::uint8_t* end_p = data + len; - while (data_p != end_p) { - data_p += fwrite(data_p, 1, end_p - data_p, file); - - if (std::ferror(file)) { - throw std::runtime_error("error during file write"); - } - } - - if (std::fflush(file)) { - throw std::runtime_error("error during file write"); - } + write_file(data, len, file); } namespace base64 { @@ -405,6 +461,7 @@ void FilesystemInstance::FileWriteString(const picojson::value& args, picojson:: const std::string& str = args.get("data").get(); size_t offset = static_cast(args.get("offset").get()); bool truncate = static_cast(args.get("truncate").get()); + const char* mode = truncate ? "w" : "r+"; const std::string& encoding = args.contains("encoding") ? args.get("encoding").get() : "utf-8"; @@ -412,11 +469,11 @@ void FilesystemInstance::FileWriteString(const picojson::value& args, picojson:: if (encoding == "iso-8859-1") { std::vector data; latin1::from_utf8(str, data); - write_file(data.data(), data.size(), location, offset, truncate); + write_file(data.data(), data.size(), location, offset, mode); } else { // default: UTF-8 const std::uint8_t* buf = (const std::uint8_t*)str.c_str(); std::size_t len = str.length(); - write_file(buf, len, location, offset, truncate); + write_file(buf, len, location, offset, mode); } } catch (std::runtime_error& e) { LoggerE("Cannot write to file %s, cause: %s", location.c_str(), e.what()); @@ -439,11 +496,12 @@ void FilesystemInstance::FileWriteBytes(const picojson::value& args, picojson::o const std::string& str = args.get("data").get(); size_t offset = static_cast(args.get("offset").get()); bool truncate = static_cast(args.get("truncate").get()); + const char* mode = truncate ? "w" : "r+"; try { std::vector data; decode_binary_from_string(str, data); - write_file(data.data(), data.size(), location, offset, truncate); + write_file(data.data(), data.size(), location, offset, mode); } catch (std::runtime_error& e) { LoggerE("Cannot write to %s, cause: %s", location.c_str(), e.what()); PrepareError(FilesystemError::Other, out); @@ -465,6 +523,7 @@ void FilesystemInstance::FileWriteBase64(const picojson::value& args, picojson:: const std::string& str = args.get("data").get(); size_t offset = static_cast(args.get("offset").get()); bool truncate = static_cast(args.get("truncate").get()); + const char* mode = truncate ? "w" : "r+"; std::vector data; try { @@ -476,7 +535,7 @@ void FilesystemInstance::FileWriteBase64(const picojson::value& args, picojson:: } try { - write_file(data.data(), data.size(), location, offset, truncate); + write_file(data.data(), data.size(), location, offset, mode); ReportSuccess(picojson::value{(double)data.size()}, out); } catch (std::runtime_error& e) { LoggerE("Cannot write to %s, cause: %s", location.c_str(), e.what()); diff --git a/src/filesystem/filesystem_instance.h b/src/filesystem/filesystem_instance.h index 07fb8fa..3457b0e 100644 --- a/src/filesystem/filesystem_instance.h +++ b/src/filesystem/filesystem_instance.h @@ -17,6 +17,17 @@ #ifndef FILESYSTEM_FILESYSTEM_INSTANCE_H_ #define FILESYSTEM_FILESYSTEM_INSTANCE_H_ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include "common/extension.h" #include "common/filesystem/filesystem_storage.h" #include "filesystem/filesystem_manager.h" @@ -25,12 +36,32 @@ namespace extension { namespace filesystem { +class FileHandle; + +typedef std::map> FileHandleMap; + +class FileHandle { + public: + FileHandle(FILE* file_handle) : file_handle(file_handle){}; + ~FileHandle(); + + FileHandle(const FileHandle&) = delete; + FileHandle(FileHandle&&) = delete; + FileHandle& operator=(const FileHandle&) = delete; + FileHandle& operator=(FileHandle&&) = delete; + + private: + FILE* file_handle; +}; + class FilesystemInstance : public common::ParsedInstance, FilesystemStateChangeListener { public: FilesystemInstance(); virtual ~FilesystemInstance(); private: + FileHandleMap opened_files; + void FileCreateSync(const picojson::value& args, picojson::object& out); void FileRename(const picojson::value& args, picojson::object& out); void FileStat(const picojson::value& args, picojson::object& out); -- 2.7.4 From 7b27168e71782488f5cf509a5abf203cbdf894d8 Mon Sep 17 00:00:00 2001 From: Arkadiusz Pietraszek Date: Thu, 19 Apr 2018 10:38:07 +0200 Subject: [PATCH 09/16] [Filesystem] Worker implementation for separate thread. ACR: http://suprem.sec.samsung.net/jira/browse/TWDAPI-121 Change-Id: I3096a27d6684c1ee4c226aebd2076c9b6f73f23c Signed-off-by: Szymon Jastrzebski Signed-off-by: Arkadiusz Pietraszek Signed-off-by: Jakub Skowron Signed-off-by: Pawel Wasowski --- src/filesystem/filesystem_instance.cc | 75 +++++++++++++++++++++++++++++++++++ src/filesystem/filesystem_instance.h | 44 ++++++++++++++++++++ 2 files changed, 119 insertions(+) diff --git a/src/filesystem/filesystem_instance.cc b/src/filesystem/filesystem_instance.cc index 33d0673..81c635c 100644 --- a/src/filesystem/filesystem_instance.cc +++ b/src/filesystem/filesystem_instance.cc @@ -53,6 +53,81 @@ FileHandle::~FileHandle() { } } +void FilesystemInstance::Worker::main() { + std::unique_lock lck{jobs_mtx}; + while (true) { + jobs_cond.wait(lck, [this] { return !jobs.empty() || exit; }); + if (exit) { + return; + } + + while (!jobs.empty()) { + auto job = jobs.front(); + jobs.pop_front(); + //////////// end of critical section + lck.unlock(); + + try { + job.func(); + } catch (...) { + // should never happen + LoggerE("Func should never throw"); + } + + try { + job.finally(); + } catch (...) { + // should never happen + LoggerE("Finally should never throw"); + } + + lck.lock(); + //////////// start of critical section + if (exit) { + return; + } + } + } +} + +void FilesystemInstance::Worker::add_job(const std::function& func, + const std::function& finally) { + { + std::lock_guard lck{jobs_mtx}; + jobs.push_back({func, finally}); + } + jobs_cond.notify_one(); +} + +FilesystemInstance::Worker::Worker() + : exit(false), thread(std::bind(&FilesystemInstance::Worker::main, this)) { +} + +FilesystemInstance::Worker::~Worker() { + { + // use memory barrier for exit flag (could be std::atomic_flag, but we use lock instead) + std::lock_guard lck{jobs_mtx}; + exit = true; + } + jobs_cond.notify_one(); + + try { + thread.join(); + } catch (std::exception& e) { + LoggerE("Failed to join thread: %s", e.what()); + } + + // finalize jobs left in queue + for (auto job : jobs) { + try { + job.finally(); + } catch (...) { + // should never happen + LoggerE("Finally should never throw"); + } + } +}; + FilesystemInstance::FilesystemInstance() { ScopeLogger(); using std::placeholders::_1; diff --git a/src/filesystem/filesystem_instance.h b/src/filesystem/filesystem_instance.h index 3457b0e..d5dc284 100644 --- a/src/filesystem/filesystem_instance.h +++ b/src/filesystem/filesystem_instance.h @@ -62,6 +62,50 @@ class FilesystemInstance : public common::ParsedInstance, FilesystemStateChangeL private: FileHandleMap opened_files; + /** + * @brief Implements single worker executing in new thread + * + * Jobs are done in order. If this worker is destroyed all pending jobs are cancelled, + * and all remaining 'finally' functions are called. + */ + class Worker { + bool exit; + struct Job { + std::function func; + std::function finally; + }; + std::deque jobs; + std::thread thread; + std::mutex jobs_mtx; + std::condition_variable jobs_cond; + void main(void); + + public: + Worker(); + ~Worker(); + + /** + * @brief Schedule a job + * Parameters will be copied (no reference is held) + * + * @param job function called as a job (should not throw) + * @param finally function called after completion or canceling. (should not throw) + */ + void add_job(const std::function& job, const std::function& finally); + + /** + * @brief Schedule a job. Same as above, but with empty finally function. + * Parameter will be copied (no reference is held) + * + * @param job function called as a job (should not throw) + */ + void add_job(const std::function& job) { + add_job(job, [] {}); + } + }; + + Worker worker; + void FileCreateSync(const picojson::value& args, picojson::object& out); void FileRename(const picojson::value& args, picojson::object& out); void FileStat(const picojson::value& args, picojson::object& out); -- 2.7.4 From b15212367cc89189c588636ef553c0941dd52c64 Mon Sep 17 00:00:00 2001 From: Lukasz Bardeli Date: Mon, 14 May 2018 09:02:25 +0200 Subject: [PATCH 10/16] [HAM] Add SLEEP_DETECTOR and STRESS_MONITOR [Verification] Code compiles without error. Tested in chrome console var listenerId; function errorCallback(error) { alert(error.name + ": " + error.message); console.log(error.name + ": " + error.message); } function listener(label) { alert("Stress level: " + label); console.log("Stress level: " + label); } var ranges = [new tizen.StressMonitorDataRange("Normal",10, 50), new tizen.StressMonitorDataRange("Stress Alarm 1",0, 20), new tizen.StressMonitorDataRange("Stress Alarm 2",50), new tizen.StressMonitorDataRange("Stress Alarm 3",30,100)]; try { listenerId = tizen.humanactivitymonitor.addStressMonitorChangeListener(ranges, listener, errorCallback); } catch (error) { console.log(error.name + ": " + error.message); } function onchangedCB(info) { alert("score: " + info.stressScore); console.log("score: " + info.stressScore); } function onerrorCB(error) { alert("Error occurred, name: " + error.name + ", message: " + error.message); console.log("Error occurred, name: " + error.name + ", message: " + error.message); } try { tizen.humanactivitymonitor.start("STRESS_MONITOR", onchangedCB, onerrorCB, {callbackInterval: 1500, sampleInterval: 100}); } catch (err) { console.log(err.name + ": " + err.message); } ACR: http://suprem.sec.samsung.net/jira/browse/TWDAPI-178 Change-Id: I56aaba87989d84d8364a8212afd98eff3d8edcee Signed-off-by: Lukasz Bardeli --- .../humanactivitymonitor_api.js | 161 ++++++++++++++++++++- .../humanactivitymonitor_extension.cc | 3 + .../humanactivitymonitor_manager.cc | 58 ++++++++ 3 files changed, 218 insertions(+), 4 deletions(-) diff --git a/src/humanactivitymonitor/humanactivitymonitor_api.js b/src/humanactivitymonitor/humanactivitymonitor_api.js index 691b953..7cc7dae 100755 --- a/src/humanactivitymonitor/humanactivitymonitor_api.js +++ b/src/humanactivitymonitor/humanactivitymonitor_api.js @@ -15,6 +15,7 @@ */ var utils_ = xwalk.utils; +var privilege_ = utils_.privilege; var type_ = utils_.type; var converter_ = utils_.converter; var validator_ = utils_.validator; @@ -43,7 +44,9 @@ var HumanActivityType = { WRIST_UP: 'WRIST_UP', HRM: 'HRM', GPS: 'GPS', - SLEEP_MONITOR: 'SLEEP_MONITOR' + SLEEP_MONITOR: 'SLEEP_MONITOR', + SLEEP_DETECTOR: 'SLEEP_DETECTOR', + STRESS_MONITOR: 'STRESS_MONITOR' }; var HumanActivityRecorderType = { @@ -108,6 +111,10 @@ function convertActivityData(type, data) { return new HumanActivityGPSInfoArray(gpsInfo); case HumanActivityType.SLEEP_MONITOR: return new HumanActivitySleepMonitorData(data); + case HumanActivityType.SLEEP_DETECTOR: + return new HumanActivitySleepDetectorData(data); + case HumanActivityType.STRESS_MONITOR: + return new HumanActivityStressMonitorData(data); default: utils_.error('Uknown human activity type: ' + type); } @@ -146,6 +153,51 @@ function convertActivityRecorderData(type, data) { return createRecorderData(func, data); } +function StressMonitorDataRange(label, min, max) { + validator_.validateConstructorCall(this, tizen.StressMonitorDataRange); + + var args = validator_.validateArgs(arguments, [ + { name: 'label', type: types_.STRING, optional: true, nullable: false }, + { name: 'min', type: types_.UNSIGNED_LONG, optional: true, nullable: false }, + { name: 'max', type: types_.UNSIGNED_LONG, optional: true, nullable: false } + ]); + + var _label = !type_.isNullOrUndefined(args.label) ? args.label : ""; + var _min = !type_.isNullOrUndefined(args.min) ? args.min : 0; + var _max = !type_.isNull(args.max) ? args.max : undefined; + + Object.defineProperties(this, { + label: { + get: function() { + return _label; + }, + set: function(v) { + _label = !type_.isNullOrUndefined(v) ? v : _label; + }, + enumerable: true + }, + min: { + get: function() { + return _min; + }, + set: function(v) { + _min = !type_.isNullOrUndefined(v) ? converter_.toUnsignedLong(v) : _min; + }, + enumerable: true + }, + max: { + get: function() { + return _max; + }, + set: function(v) { + _max = !type_.isNullOrUndefined(v) ? converter_.toUnsignedLong(v) : _max; + }, + enumerable: true + } + }); +}; + + function ActivityRecognitionListenerManager() { this.listeners = {}; this.nextId = 1; @@ -288,6 +340,8 @@ function GPSCallback(result) { } } +var stressListener = null; + HumanActivityMonitorManager.prototype.start = function(type, changedCallback) { var args = validator_.validateArgs(arguments, [ {name: 'type', type: types_.ENUM, values: Object.keys(HumanActivityType)}, @@ -296,7 +350,7 @@ HumanActivityMonitorManager.prototype.start = function(type, changedCallback) { {name: 'options', type : types_.DICTIONARY, optional : true, nullable : true} ]); - var listenerId = 'HumanActivityMonitor_' + args.type; + var listenerId = 'HumanActivityMonitor_' + args.type; var optionsAttributes = ["callbackInterval", "sampleInterval"], options = args.options || {}; var callbackInterval = null, sampleInterval = null; @@ -326,6 +380,9 @@ HumanActivityMonitorManager.prototype.start = function(type, changedCallback) { case HumanActivityType.GPS: listener = GPSCallback; break; + case HumanActivityType.STRESS_MONITOR: + listener = stressMonitorListener.onListener; + break; default: listener = function(result) { native_.callIfPossible(args.changedCallback, convertActivityData(args.type, result)); @@ -347,7 +404,7 @@ HumanActivityMonitorManager.prototype.start = function(type, changedCallback) { pedometerListener = args.changedCallback; } - if (HumanActivityType.GPS === args.type) { + if (HumanActivityType.GPS === args.type || HumanActivityType.STRESS_MONITOR === args.type) { var callback = function(result) { if (native_.isFailure(result)) { native_.callIfPossible(args.errorCallback, native_.getErrorObject(result)); @@ -356,7 +413,11 @@ HumanActivityMonitorManager.prototype.start = function(type, changedCallback) { } }; - GPSListener = callback; + if (HumanActivityType.GPS === args.type) { + GPSListener = callback; + } else if (HumanActivityType.STRESS_MONITOR === args.type){ + stressListener = callback; + } } }; @@ -380,6 +441,10 @@ HumanActivityMonitorManager.prototype.stop = function(type) { if (HumanActivityType.GPS === args.type) { GPSListener = null; } + + if (HumanActivityType.STRESS_MONITOR === args.type) { + stressListener = null; + } }; HumanActivityMonitorManager.prototype.setAccumulativePedometerListener = function() { @@ -675,6 +740,78 @@ HumanActivityMonitorManager.prototype.removeGestureRecognitionListener = functio gestureRecognitionListener.removeListener(args.watchId); }; +function StressMonitorListenerManager() { + this.listeners = {}; + this.nextId = 1; +}; + +StressMonitorListenerManager.prototype.onListener = function(data) { + + if (stressListener) { + stressListener(data); + } + var score = data.stressScore; + for (var watchId in stressMonitorListener.listeners) { + if (stressMonitorListener.listeners.hasOwnProperty(watchId)) { + var _listener = stressMonitorListener.listeners[watchId]; + var rangeArray = _listener.ranges; + for (var id in rangeArray) { + var _min = rangeArray[id].min; + var _max = !type_.isUndefined(rangeArray[id].max) ? rangeArray[id].max : Number.MAX_VALUE; + if ((score >= _min && score < _max) && (_listener.lastStressScore < _min || _listener.lastStressScore >= _max)) { + _listener.listener(rangeArray[id].label); + } + } + _listener.lastStressScore = score; + } + } +}; + +StressMonitorListenerManager.prototype.addListener = function(ranges, listener, errorCallback) { + + var id = this.nextId++; + + this.listeners[id] = { + ranges: ranges, + listener: listener, + lastStressScore: -1 + }; + + return id; +}; + +StressMonitorListenerManager.prototype.removeListener = function(watchId) { + if (this.listeners.hasOwnProperty(watchId)) { + delete this.listeners[watchId]; + } +}; + +var stressMonitorListener = new StressMonitorListenerManager(); + +HumanActivityMonitorManager.prototype.addStressMonitorChangeListener = function() { + utils_.checkPrivilegeAccess(privilege_.HEALTHINFO); + var args = validator_.validateMethod(arguments, [{ + name : 'ranges', + type: types_.ARRAY, + values: StressMonitorDataRange + }, + { + name : 'listener', + type : types_.FUNCTION + }]); + + return stressMonitorListener.addListener(args.ranges, args.listener); +}; + +HumanActivityMonitorManager.prototype.removeStressMonitorChangeListener = function() { + var args = validator_.validateMethod(arguments, [{ + name : 'watchId', + type : types_.LONG, + }]); + + stressMonitorListener.removeListener(args.watchId); +}; + function StepDifference(data) { SetReadOnlyProperty(this, 'stepCountDifference', data.stepCountDifference); SetReadOnlyProperty(this, 'timestamp', data.timestamp); @@ -769,6 +906,20 @@ function HumanActivitySleepMonitorData(data) { HumanActivitySleepMonitorData.prototype = new HumanActivityData(); HumanActivitySleepMonitorData.prototype.constructor = HumanActivitySleepMonitorData; +function HumanActivitySleepDetectorData(data) { + SetReadOnlyProperty(this, 'status', data.status); +} + +HumanActivitySleepDetectorData.prototype = new HumanActivityData(); +HumanActivitySleepDetectorData.prototype.constructor = HumanActivitySleepMonitorData; + +function HumanActivityStressMonitorData(data) { + SetReadOnlyProperty(this, 'stressScore', data.stressScore); +} + +HumanActivityStressMonitorData.prototype = new HumanActivityData(); +HumanActivityStressMonitorData.prototype.constructor = HumanActivityStressMonitorData + //Recorded data function HumanActivityRecorderData(data) { if (data) { @@ -831,4 +982,6 @@ function GestureData(data) { HumanActivityRecorderPressureData.prototype = new HumanActivityRecorderData(); HumanActivityRecorderPressureData.prototype.constructor = HumanActivityRecorderPressureData; +tizen.StressMonitorDataRange = StressMonitorDataRange; + exports = new HumanActivityMonitorManager(); diff --git a/src/humanactivitymonitor/humanactivitymonitor_extension.cc b/src/humanactivitymonitor/humanactivitymonitor_extension.cc index 6074271..e17fc87 100644 --- a/src/humanactivitymonitor/humanactivitymonitor_extension.cc +++ b/src/humanactivitymonitor/humanactivitymonitor_extension.cc @@ -27,6 +27,9 @@ common::Extension* CreateExtension() { HumanActivityMonitorExtension::HumanActivityMonitorExtension() { SetExtensionName("tizen.humanactivitymonitor"); SetJavaScriptAPI(kSource_humanactivitymonitor_api); + + const char* entry_points[] = {"tizen.StressMonitorDataRange", NULL}; + SetExtraJSEntryPoints(entry_points); } HumanActivityMonitorExtension::~HumanActivityMonitorExtension() { diff --git a/src/humanactivitymonitor/humanactivitymonitor_manager.cc b/src/humanactivitymonitor/humanactivitymonitor_manager.cc index 457c398..cff3058 100644 --- a/src/humanactivitymonitor/humanactivitymonitor_manager.cc +++ b/src/humanactivitymonitor/humanactivitymonitor_manager.cc @@ -48,6 +48,8 @@ const std::string kActivityTypeWristUp = "WRIST_UP"; const std::string kActivityTypeHrm = "HRM"; const std::string kActivityTypeSleepMonitor = "SLEEP_MONITOR"; const std::string kActivityTypePressure = "PRESSURE"; +const std::string kActivityTypeSleepDetector = "SLEEP_DETECTOR"; +const std::string kActivityTypeStressMonitor = "STRESS_MONITOR"; const std::string kSleepStateAwake = "AWAKE"; const std::string kSleepStateAsleep = "ASLEEP"; @@ -58,6 +60,7 @@ const std::string kSampleInterval = "sampleInterval"; const std::string kStatus = "status"; const std::string kTimestamp = "timestamp"; +const std::string kStressScore = "stressScore"; const std::string kStepStatus = "stepStatus"; const std::string kSpeed = "speed"; @@ -1445,6 +1448,53 @@ HumanActivityMonitorManager::HumanActivityMonitorManager() return ConvertRecordedTime(data, obj); }; + auto convert_sleep_detector = [](sensor_event_s* event, + picojson::object* data) -> PlatformResult { + ScopeLogger("convert_sleep_detector"); + + if (event->value_count < 1) { + return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "To few values of SLEEP event"); + } + + sensor_sleep_state_e state = static_cast(event->values[0]); + std::string sleep_state; + + switch (state) { + case SENSOR_SLEEP_STATE_WAKE: + sleep_state = kSleepStateAwake; + break; + + case SENSOR_SLEEP_STATE_SLEEP: + sleep_state = kSleepStateAsleep; + break; + + case SENSOR_SLEEP_STATE_UNKNOWN: + sleep_state = kSleepStateUnknown; + break; + + default: + return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Unknown sleep state", + ("Unknown sleep state: %d", state)); + } + + data->insert(std::make_pair(kStatus, picojson::value(sleep_state))); + + return PlatformResult(ErrorCode::NO_ERROR); + }; + + auto convert_stress = [](sensor_event_s* event, picojson::object* data) -> PlatformResult { + ScopeLogger("convert_stress"); + + if (event->value_count < 1) { + return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "To few values of STRESS event"); + } + + float stress_score = event->values[0]; + data->insert(std::make_pair(kStressScore, picojson::value(static_cast(stress_score)))); + + return PlatformResult(ErrorCode::NO_ERROR); + }; + auto convert_recorded_hrm = [](void* data, picojson::object* obj) -> PlatformResult { ScopeLogger("Entered into asynchronous function, convert_recorded_hrm"); @@ -1494,6 +1544,14 @@ HumanActivityMonitorManager::HumanActivityMonitorManager() convert_pedometer, convert_recorded_pedometer))); monitors_.insert(std::make_pair(kActivityTypeWristUp, std::make_shared(kActivityTypeWristUp))); + monitors_.insert(std::make_pair(kActivityTypeSleepDetector, + std::make_shared( + kActivityTypeSleepDetector, SENSOR_HUMAN_SLEEP_DETECTOR, + convert_sleep_detector, nullptr))); + monitors_.insert(std::make_pair( + kActivityTypeStressMonitor, + std::make_shared( + kActivityTypeStressMonitor, SENSOR_HUMAN_STRESS_MONITOR, convert_stress, nullptr))); monitors_.insert(std::make_pair( kActivityTypeHrm, std::make_shared( kActivityTypeHrm, SENSOR_HRM, convert_hrm, convert_recorded_hrm))); -- 2.7.4 From a01f2fb5120d342cdcc5c0e672b7377b21a71619 Mon Sep 17 00:00:00 2001 From: Arkadiusz Pietraszek Date: Thu, 19 Apr 2018 11:23:58 +0200 Subject: [PATCH 11/16] [Filesystem] Filesystem handling methods implementation. ACR: http://suprem.sec.samsung.net/jira/browse/TWDAPI-121 Change-Id: I84f383a2f5a8c56a9b21ad2bbc3cc872ee86e427 Signed-off-by: Szymon Jastrzebski Signed-off-by: Arkadiusz Pietraszek Signed-off-by: Jakub Skowron Signed-off-by: Pawel Wasowski --- src/filesystem/filesystem_instance.cc | 517 +++++++++++++++++++++++++++++++++- src/filesystem/filesystem_instance.h | 15 + 2 files changed, 524 insertions(+), 8 deletions(-) diff --git a/src/filesystem/filesystem_instance.cc b/src/filesystem/filesystem_instance.cc index 81c635c..c8faf71 100644 --- a/src/filesystem/filesystem_instance.cc +++ b/src/filesystem/filesystem_instance.cc @@ -126,14 +126,16 @@ FilesystemInstance::Worker::~Worker() { LoggerE("Finally should never throw"); } } -}; +} FilesystemInstance::FilesystemInstance() { ScopeLogger(); + using std::placeholders::_1; using std::placeholders::_2; #define REGISTER_SYNC(c, x) RegisterSyncHandler(c, std::bind(&FilesystemInstance::x, this, _1, _2)); + REGISTER_SYNC("File_stat", FileStat); REGISTER_SYNC("File_statSync", FileStatSync); REGISTER_SYNC("File_createSync", FileCreateSync); @@ -154,6 +156,21 @@ FilesystemInstance::FilesystemInstance() { REGISTER_SYNC("File_removeDirectory", RemoveDirectory); REGISTER_SYNC("File_copyTo", CopyTo); REGISTER_SYNC("FileSystemManager_getCanonicalPath", FileSystemManagerGetCanonicalPath); + + REGISTER_SYNC("FileSystemManager_createDirectory", FileSystemManagerCreateDirectory); + REGISTER_SYNC("FileSystemManager_deleteFile", FileSystemManagerDeleteFile); + REGISTER_SYNC("FileSystemManager_deleteDirectory", FileSystemManagerDeleteDirectory); + REGISTER_SYNC("FileSystemManager_copyFile", FileSystemManagerCopyFile); + REGISTER_SYNC("FileSystemManager_copyDirectory", FileSystemManagerCopyDirectory); + REGISTER_SYNC("FileSystemManager_moveFile", FileSystemManagerMoveFile); + REGISTER_SYNC("FileSystemManager_moveDirectory", FileSystemManagerMoveDirectory); + REGISTER_SYNC("FileSystemManager_rename", FileSystemManagerRename); + REGISTER_SYNC("FileSystemManager_listDirectory", FileSystemManagerListDirectory); + REGISTER_SYNC("FileSystemManager_isFile", FileSystemManagerIsFile); + REGISTER_SYNC("FileSystemManager_isDirectory", FileSystemManagerIsDirectory); + REGISTER_SYNC("FileSystemManager_pathExists", FileSystemManagerPathExists); + REGISTER_SYNC("FileSystemManager_getLimits", FileSystemManagerGetLimits); + #undef REGISTER_SYNC FilesystemManager::GetInstance().AddListener(this); } @@ -300,7 +317,7 @@ static std::vector read_file(std::string path, long offset = 0, FILE* file = std::fopen(path.c_str(), "r"); if (!file) { - std::string err_msg = std::string("cannot open file to read") + strerror(errno); + std::string err_msg = std::string("Cannot open file to read. ") + strerror(errno); throw std::system_error{errno, std::generic_category(), err_msg}; } @@ -312,7 +329,7 @@ static std::vector read_file(std::string path, long offset = 0, }; if (0 != offset && 0 != std::fseek(file, offset, SEEK_SET)) { - std::string err_msg = std::string("cannot perform seek") + strerror(errno); + std::string err_msg = std::string("Cannot perform seek. ") + strerror(errno); throw std::system_error{errno, std::generic_category(), err_msg}; } @@ -342,7 +359,7 @@ static std::vector read_file(FILE* file, std::size_t length /*= NP data_p += std::fread(data_p, 1, end_p - data_p, file); if (std::ferror(file)) { - std::string err_msg = std::string("error during file read") + strerror(errno); + std::string err_msg = std::string("Error during file read. ") + strerror(errno); throw std::runtime_error(err_msg); } @@ -366,13 +383,13 @@ void write_file(const std::uint8_t* data, std::size_t len, FILE* file) { data_p += fwrite(data_p, 1, end_p - data_p, file); if (std::ferror(file)) { - std::string err_msg = std::string("error during file write") + strerror(errno); + std::string err_msg = std::string("Error during file write. ") + strerror(errno); throw std::runtime_error(err_msg); } } if (std::fflush(file)) { - std::string err_msg = std::string("error during file write") + strerror(errno); + std::string err_msg = std::string("Error during file write. ") + strerror(errno); throw std::runtime_error(err_msg); } } @@ -387,7 +404,7 @@ void write_file(const std::uint8_t* data, std::size_t len, std::string path, lon FILE* file = std::fopen(path.c_str(), mode); if (!file) { - std::string err_msg = std::string("cannot open file to write") + strerror(errno); + std::string err_msg = std::string("Cannot open file to write. ") + strerror(errno); throw std::runtime_error(err_msg); } @@ -399,7 +416,7 @@ void write_file(const std::uint8_t* data, std::size_t len, std::string path, lon }; if (offset != 0 && std::fseek(file, offset, SEEK_SET) != 0) { - std::string err_msg = std::string("cannot perform seek") + strerror(errno); + std::string err_msg = std::string("Cannot perform seek. ") + strerror(errno); throw std::system_error{errno, std::generic_category(), err_msg}; } @@ -1001,6 +1018,490 @@ void FilesystemInstance::FileSystemManagerGetCanonicalPath(const picojson::value FilesystemManager::GetInstance().GetCanonicalPath(path, onSuccess, onError); } +void FilesystemInstance::FileSystemManagerCreateDirectory(const picojson::value& args, + picojson::object& out) { + ScopeLogger(); + CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemWrite, &out); + + double callback_id = args.get("callbackId").get(); + const std::string& path = args.get("path").get(); + bool make_parents = args.get("makeParents").get(); + + this->worker.add_job([this, callback_id, path, make_parents] { + picojson::value response = picojson::value(picojson::object()); + picojson::object& obj = response.get(); + obj["callbackId"] = picojson::value(callback_id); + + try { + FilesystemUtils::Mkdir(path, make_parents); + ReportSuccess(obj); + } catch (const std::system_error& e) { + FilesystemUtils::TranslateException(e, obj); + } + this->PostMessage(response.serialize().c_str()); + }); + + ReportSuccess(out); +} + +void FilesystemInstance::FileSystemManagerDeleteFile(const picojson::value& args, + picojson::object& out) { + ScopeLogger(); + CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemWrite, &out); + + double callback_id = args.get("callbackId").get(); + const std::string& path = args.get("path").get(); + + this->worker.add_job([this, callback_id, path] { + picojson::value response = picojson::value(picojson::object()); + picojson::object& obj = response.get(); + obj["callbackId"] = picojson::value(callback_id); + + try { + FilesystemUtils::Unlink(path); + ReportSuccess(obj); + } catch (const std::system_error& e) { + FilesystemUtils::TranslateException(e, obj); + } + this->PostMessage(response.serialize().c_str()); + }); + + ReportSuccess(out); +} + +void FilesystemInstance::FileSystemManagerDeleteDirectory(const picojson::value& args, + picojson::object& out) { + ScopeLogger(); + CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemWrite, &out); + + double callback_id = args.get("callbackId").get(); + const std::string& path = args.get("path").get(); + bool recursive = args.get("recursive").get(); + + this->worker.add_job([this, callback_id, path, recursive] { + ScopeLogger(); + picojson::value response = picojson::value(picojson::object()); + picojson::object& obj = response.get(); + obj["callbackId"] = picojson::value(callback_id); + + try { + struct stat buf {}; + if (!FilesystemUtils::CheckIfExists(path, &buf) || !FilesystemUtils::CheckIfDir(buf)) { + LogAndReportError(NotFoundException("Given path does not point to directory."), obj); + return; + } + if (recursive) { + FilesystemUtils::RemoveDirectoryRecursively(path); + } else { + FilesystemUtils::RemoveDirectory(path); + } + ReportSuccess(obj); + } catch (const std::system_error& e) { + FilesystemUtils::TranslateException(e, obj); + } + this->PostMessage(response.serialize().c_str()); + }); + + ReportSuccess(out); +} + +void FilesystemInstance::FileSystemManagerCopyFile(const picojson::value& args, + picojson::object& out) { + // TODO: currently does not create file with the same name as src if dest is a directory + ScopeLogger(); + CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemWrite, &out); + CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemRead, &out); + + double callback_id = args.get("callbackId").get(); + const std::string& path = args.get("path").get(); + const std::string& destination_path = args.get("destinationPath").get(); + bool overwrite = args.get("overwrite").get(); + + this->worker.add_job([this, callback_id, path, destination_path, overwrite] { + picojson::value response = picojson::value(picojson::object()); + picojson::object& obj = response.get(); + obj["callbackId"] = picojson::value(callback_id); + + try { + struct stat buf {}; + if (!FilesystemUtils::CheckIfExists(path, &buf) || !FilesystemUtils::CheckIfFile(buf)) { + LogAndReportError(NotFoundException("Given path does not point to file."), obj); + return; + } + buf = {}; + if (!FilesystemUtils::CheckIfExists(destination_path, &buf) || + !FilesystemUtils::CheckIfDir(buf)) { + LogAndReportError(NotFoundException("Given path does not point to directory."), obj); + return; + } + + buf = {}; + std::string new_path = destination_path + '/' + FilesystemUtils::PosixBasename(path); + if (!overwrite && FilesystemUtils::CheckIfExists(new_path, &buf)) { + LogAndReportError(IOException("File or directory with conflicting name already exists."), + obj); + return; + } + FilesystemUtils::CopyFile(path, destination_path, overwrite); + ReportSuccess(obj); + } catch (const std::system_error& e) { + FilesystemUtils::TranslateException(e, obj); + } + this->PostMessage(response.serialize().c_str()); + }); + + ReportSuccess(out); +} + +void FilesystemInstance::FileSystemManagerCopyDirectory(const picojson::value& args, + picojson::object& out) { + ScopeLogger(); + CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemRead, &out); + CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemWrite, &out); + const std::string& path = args.get("path").get(); + const std::string& destination_path = args.get("destinationPath").get(); + double callback_id = args.get("callbackId").get(); + bool overwrite = args.get("overwrite").get(); + + this->worker.add_job([this, callback_id, path, destination_path, overwrite] { + ScopeLogger(); + picojson::value response = picojson::value(picojson::object()); + picojson::object& obj = response.get(); + obj["callbackId"] = picojson::value(callback_id); + + try { + struct stat buf {}; + if (!FilesystemUtils::CheckIfExists(path, &buf) || !FilesystemUtils::CheckIfDir(buf)) { + LogAndReportError(NotFoundException("Given path does not point to directory."), obj); + return; + } + buf = {}; + if (!FilesystemUtils::CheckIfExists(destination_path, &buf) || + !FilesystemUtils::CheckIfDir(buf)) { + LogAndReportError(NotFoundException("Given path does not point to directory."), obj); + return; + } + std::string new_path = destination_path + '/' + FilesystemUtils::PosixBasename(path); + buf = {}; + if (FilesystemUtils::CheckIfExists(new_path, &buf) && !FilesystemUtils::CheckIfDir(buf)) { + LogAndReportError(IOException("File with conflicting name already exists."), obj); + return; + } + FilesystemUtils::CopyDirectory(path, destination_path, overwrite); + ReportSuccess(obj); + } catch (const std::system_error& e) { + FilesystemUtils::TranslateException(e, obj); + } + this->PostMessage(response.serialize().c_str()); + }); + + ReportSuccess(out); +} + +void FilesystemInstance::FileSystemManagerMoveFile(const picojson::value& args, + picojson::object& out) { + ScopeLogger(); + CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemRead, &out); + CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemWrite, &out); + + const std::string& path = args.get("path").get(); + const std::string& destination_path = args.get("destinationPath").get(); + double callback_id = args.get("callbackId").get(); + bool overwrite = args.get("overwrite").get(); + + this->worker.add_job([this, callback_id, path, destination_path, overwrite] { + ScopeLogger(); + picojson::value response = picojson::value(picojson::object()); + picojson::object& obj = response.get(); + obj["callbackId"] = picojson::value(callback_id); + + try { + struct stat buf {}; + if (!FilesystemUtils::CheckIfExists(path, &buf) || !FilesystemUtils::CheckIfFile(buf)) { + LogAndReportError(NotFoundException("Given path does not point to file."), obj); + return; + } + buf = {}; + if (!FilesystemUtils::CheckIfExists(destination_path, &buf) || + !FilesystemUtils::CheckIfDir(buf)) { + LogAndReportError(NotFoundException("Given path does not point to directory."), obj); + return; + } + + buf = {}; + std::string new_path = destination_path + '/' + FilesystemUtils::PosixBasename(path); + if (!overwrite && FilesystemUtils::CheckIfExists(new_path, &buf)) { + LogAndReportError(IOException("File or directory with conflicting name already exists."), + obj); + return; + } + FilesystemUtils::MoveFile(path, new_path, overwrite); + ReportSuccess(obj); + } catch (const std::system_error& e) { + FilesystemUtils::TranslateException(e, obj); + } + this->PostMessage(response.serialize().c_str()); + }); + + ReportSuccess(out); +} + +void FilesystemInstance::FileSystemManagerMoveDirectory(const picojson::value& args, + picojson::object& out) { + ScopeLogger(); + + CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemRead, &out); + CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemWrite, &out); + double callback_id = args.get("callbackId").get(); + const std::string& path = args.get("path").get(); + const std::string& destination_path = args.get("destinationPath").get(); + bool overwrite = args.get("overwrite").get(); + + this->worker.add_job([this, callback_id, path, destination_path, overwrite] { + picojson::value response = picojson::value(picojson::object()); + picojson::object& obj = response.get(); + obj["callbackId"] = picojson::value(callback_id); + + try { + struct stat buf {}; + if (!FilesystemUtils::CheckIfExists(path, &buf) || !FilesystemUtils::CheckIfDir(buf)) { + LogAndReportError(NotFoundException("Given path does not point to directory."), obj); + return; + } + buf = {}; + if (!FilesystemUtils::CheckIfExists(destination_path, &buf) || + !FilesystemUtils::CheckIfDir(buf)) { + LogAndReportError(NotFoundException("Given path does not point to directory."), obj); + return; + } + buf = {}; + std::string new_path = destination_path + '/' + FilesystemUtils::PosixBasename(path); + if (FilesystemUtils::CheckIfExists(new_path, &buf) && !FilesystemUtils::CheckIfDir(buf)) { + LogAndReportError(IOException("File or directory with conflicting name already exists."), obj); + return; + } + FilesystemUtils::MoveDirectory(path, destination_path, overwrite); + ReportSuccess(obj); + } catch (const std::system_error& e) { + FilesystemUtils::TranslateException(e, obj); + } + this->PostMessage(response.serialize().c_str()); + }); + + ReportSuccess(out); +} + +void FilesystemInstance::FileSystemManagerRename(const picojson::value& args, + picojson::object& out) { + ScopeLogger(); + CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemWrite, &out); + const std::string& path = args.get("path").get(); + double callback_id = args.get("callbackId").get(); + const std::string& new_name = args.get("newName").get(); + + this->worker.add_job([this, callback_id, new_name, path] { + ScopeLogger(); + picojson::value response = picojson::value(picojson::object()); + picojson::object& obj = response.get(); + obj["callbackId"] = picojson::value(callback_id); + + try { + struct stat buf {}; + bool exists = FilesystemUtils::CheckIfExists(path, &buf); + if (!exists) { + LogAndReportError(NotFoundException("Given path does not point to file or directory."), + obj); + return; + } + std::string new_path{FilesystemUtils::Dirname(path) + "/" + new_name}; + buf = {}; + exists = FilesystemUtils::CheckIfExists(new_path, &buf); + if (exists) { + LogAndReportError(IOException("File or directory with conflicting name already exists"), + obj); + return; + } + FilesystemUtils::Rename(path, new_path); + ReportSuccess(obj); + } catch (const std::system_error& e) { + FilesystemUtils::TranslateException(e, obj); + } + this->PostMessage(response.serialize().c_str()); + }); + + ReportSuccess(out); +} + +void FilterResult(std::vector& names, std::vector& types, bool is_type, + unsigned char type) { + int i = (int)names.size() - 1; + + while (i >= 0) { + if (is_type ? type != types[i] : type == types[i]) { + names.erase(names.begin() + i); + } + i--; + } +} + +void FilesystemInstance::FileSystemManagerListDirectory(const picojson::value& args, + picojson::object& out) { + ScopeLogger(); + CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemRead, &out); + + double callback_id = args.get("callbackId").get(); + const std::string& path = args.get("path").get(); + const picojson::object& filter = args.get("filter").get(); + + this->worker.add_job([this, callback_id, path, filter] { + ScopeLogger(); + picojson::value response = picojson::value(picojson::object()); + picojson::object& obj = response.get(); + obj["callbackId"] = picojson::value(callback_id); + + try { + std::vector names; + { + std::vector types; + FilesystemUtils::ListDirectory(path, [&](const char* name, unsigned char type) { + names.push_back(name); + types.push_back(type); + }); + + auto it = filter.find("isFile"); + if (filter.end() != it) { + FilterResult(names, types, it->second.get(), DT_REG); + } + + it = filter.find("isDirectory"); + if (filter.end() != it) { + FilterResult(names, types, it->second.get(), DT_DIR); + } + } + + auto start_modified_it = filter.find("startModified"), + end_modified_it = filter.find("endModified"), + start_created_it = filter.find("startCreated"), + end_created_it = filter.find("endCreated"); + if (filter.end() != start_modified_it || filter.end() != end_modified_it || + filter.end() != start_created_it || filter.end() != end_created_it) { + auto name_iterator = names.begin(); + while (name_iterator != names.end()) { + struct ::stat buf; + std::string path_with_name = path + std::string("/") + std::string(*name_iterator); + int status = ::stat(path_with_name.c_str(), &buf); + if (status != 0) { + throw std::system_error{errno, std::generic_category(), + "Failed to get last modification date of a file"}; + } + if (filter.end() != start_modified_it && + (buf.st_mtime < start_modified_it->second.get())) { + name_iterator = names.erase(name_iterator); + continue; + } + if (filter.end() != end_modified_it && + (buf.st_mtime > end_modified_it->second.get())) { + name_iterator = names.erase(name_iterator); + continue; + } + if (filter.end() != start_created_it && + (buf.st_ctime < start_created_it->second.get())) { + name_iterator = names.erase(name_iterator); + continue; + } + if (filter.end() != end_created_it && + (buf.st_ctime > end_created_it->second.get())) { + name_iterator = names.erase(name_iterator); + continue; + } + name_iterator++; + } + } + + picojson::value value{picojson::array_type, true}; + picojson::array& names_array = value.get(); + + for (unsigned int i = 0; i < names.size(); ++i) { + names_array.push_back(picojson::value(names[i])); + } + + ReportSuccess(value, obj); + } catch (const std::system_error& e) { + FilesystemUtils::TranslateException(e, obj); + } + this->PostMessage(response.serialize().c_str()); + }); + + ReportSuccess(out); +} + +void FilesystemInstance::FileSystemManagerIsFile(const picojson::value& args, + picojson::object& out) { + ScopeLogger(); + CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemRead, &out); + const std::string& path = args.get("path").get(); + picojson::value is_file{}; + try { + struct stat buf {}; + bool exists = FilesystemUtils::CheckIfExists(path, &buf); + if (!exists) { + LogAndReportError(NotFoundException("Given path does not point to file."), out); + return; + } + is_file = picojson::value{FilesystemUtils::CheckIfFile(buf)}; + } catch (const std::system_error& e) { + FilesystemUtils::TranslateException(e, out); + } + ReportSuccess(is_file, out); +} + +void FilesystemInstance::FileSystemManagerIsDirectory(const picojson::value& args, + picojson::object& out) { + ScopeLogger(); + CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemRead, &out); + const std::string& path = args.get("path").get(); + picojson::value is_directory{}; + try { + struct stat buf {}; + bool exists = FilesystemUtils::CheckIfExists(path, &buf); + if (!exists) { + LogAndReportError(NotFoundException("Given path does not point to directory."), out); + return; + } + is_directory = picojson::value{FilesystemUtils::CheckIfDir(buf)}; + } catch (const std::system_error& e) { + FilesystemUtils::TranslateException(e, out); + } + ReportSuccess(is_directory, out); +} + +void FilesystemInstance::FileSystemManagerPathExists(const picojson::value& args, + picojson::object& out) { + ScopeLogger(); + CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemRead, &out); + const std::string& path = args.get("path").get(); + picojson::value does_file_exist = picojson::value{true}; + try { + struct stat buf {}; + bool exists = FilesystemUtils::CheckIfExists(path, &buf); + if (!exists) { + does_file_exist = picojson::value{false}; + } + } catch (const std::system_error& e) { + FilesystemUtils::TranslateException(e, out); + } + ReportSuccess(does_file_exist, out); +} + +void FilesystemInstance::FileSystemManagerGetLimits(const picojson::value& args, + picojson::object& out) { + ScopeLogger(); + picojson::value response = + picojson::value{picojson::array{picojson::value{static_cast(NAME_MAX)}, + picojson::value{static_cast(PATH_MAX)}}}; + ReportSuccess(response, out); +} + #undef CHECK_EXIST } // namespace filesystem diff --git a/src/filesystem/filesystem_instance.h b/src/filesystem/filesystem_instance.h index d5dc284..dbe9ee7 100644 --- a/src/filesystem/filesystem_instance.h +++ b/src/filesystem/filesystem_instance.h @@ -129,6 +129,21 @@ class FilesystemInstance : public common::ParsedInstance, FilesystemStateChangeL void onFilesystemStateChangeSuccessCallback(const common::Storage& storage); void PrepareError(const FilesystemError& error, picojson::object& out); void FileSystemManagerGetCanonicalPath(const picojson::value& args, picojson::object& out); + + void FileSystemManagerCreateDirectory(const picojson::value& args, picojson::object& out); + void FileSystemManagerDeleteFile(const picojson::value& args, picojson::object& out); + void FileSystemManagerDeleteDirectory(const picojson::value& args, picojson::object& out); + void FileSystemManagerCopyFile(const picojson::value& args, picojson::object& out); + void FileSystemManagerCopyDirectory(const picojson::value& args, picojson::object& out); + void FileSystemManagerMoveFile(const picojson::value& args, picojson::object& out); + void FileSystemManagerMoveDirectory(const picojson::value& args, picojson::object& out); + void FileSystemManagerRename(const picojson::value& args, picojson::object& out); + void FileSystemManagerListDirectory(const picojson::value& args, picojson::object& out); + void FileSystemManagerIsFile(const picojson::value& args, picojson::object& out); + void FileSystemManagerIsDirectory(const picojson::value& args, picojson::object& out); + void FileSystemManagerPathExists(const picojson::value& args, picojson::object& out); + void FileSystemManagerToURI(const picojson::value& args, picojson::object& out); + void FileSystemManagerGetLimits(const picojson::value& args, picojson::object& out); }; } // namespace filesystem -- 2.7.4 From ceac018864180f530797ac87537dd799036cceb5 Mon Sep 17 00:00:00 2001 From: Arkadiusz Pietraszek Date: Thu, 19 Apr 2018 12:35:16 +0200 Subject: [PATCH 12/16] [Filesystem] FileHandle handling methods implementation. ACR: http://suprem.sec.samsung.net/jira/browse/TWDAPI-121 Change-Id: Ie0bc67b1858ad9638942d9a0c93ee799316c05ee Signed-off-by: Szymon Jastrzebski Signed-off-by: Arkadiusz Pietraszek Signed-off-by: Jakub Skowron Signed-off-by: Pawel Wasowski --- src/filesystem/filesystem_instance.cc | 822 ++++++++++++++++++++++++++++++++++ src/filesystem/filesystem_instance.h | 52 ++- 2 files changed, 872 insertions(+), 2 deletions(-) diff --git a/src/filesystem/filesystem_instance.cc b/src/filesystem/filesystem_instance.cc index c8faf71..5c0f341 100644 --- a/src/filesystem/filesystem_instance.cc +++ b/src/filesystem/filesystem_instance.cc @@ -38,6 +38,45 @@ namespace { // The privileges that required in Filesystem API const std::string kPrivilegeFilesystemRead = "http://tizen.org/privilege/filesystem.read"; const std::string kPrivilegeFilesystemWrite = "http://tizen.org/privilege/filesystem.write"; + +std::string GetFopenMode(const picojson::value& args) { + ScopeLogger(); + const std::string& open_mode = args.get("openMode").get(); + + if ("rw" == open_mode) { + return "r+"; + } else if ("rwo" == open_mode) { + return "w+"; + } + + return open_mode; +} + +bool WriteAccessRequested(const picojson::value& args) { + ScopeLogger(); + const std::string& open_mode = args.get("openMode").get(); + + return std::string::npos != open_mode.find("w") || "a" == open_mode; +} + +bool ReadAccessRequested(const picojson::value& args) { + ScopeLogger(); + const std::string& open_mode = args.get("openMode").get(); + + return std::string::npos != open_mode.find("r"); +} + +bool ShouldMakeParents(const picojson::value& args) { + ScopeLogger(); + const std::string& path = args.get("path").get(); + struct stat buf {}; + if (FilesystemUtils::CheckIfExists(FilesystemUtils::Dirname(path), &buf)) { + return false; + } + bool make_parents = args.get("makeParents").get(); + const std::string& open_mode = args.get("openMode").get(); + return make_parents && ("w" == open_mode || "a" == open_mode || "rwo" == open_mode); +} } using namespace common; @@ -157,6 +196,7 @@ FilesystemInstance::FilesystemInstance() { REGISTER_SYNC("File_copyTo", CopyTo); REGISTER_SYNC("FileSystemManager_getCanonicalPath", FileSystemManagerGetCanonicalPath); + REGISTER_SYNC("FileSystemManager_openFile", FileSystemManagerOpenFile); REGISTER_SYNC("FileSystemManager_createDirectory", FileSystemManagerCreateDirectory); REGISTER_SYNC("FileSystemManager_deleteFile", FileSystemManagerDeleteFile); REGISTER_SYNC("FileSystemManager_deleteDirectory", FileSystemManagerDeleteDirectory); @@ -171,6 +211,15 @@ FilesystemInstance::FilesystemInstance() { REGISTER_SYNC("FileSystemManager_pathExists", FileSystemManagerPathExists); REGISTER_SYNC("FileSystemManager_getLimits", FileSystemManagerGetLimits); + REGISTER_SYNC("FileHandle_seek", FileHandleSeek); + REGISTER_SYNC("FileHandle_readString", FileHandleReadString); + REGISTER_SYNC("FileHandle_writeString", FileHandleWriteString); + REGISTER_SYNC("FileHandle_readData", FileHandleReadData); + REGISTER_SYNC("FileHandle_writeData", FileHandleWriteData); + REGISTER_SYNC("FileHandle_flush", FileHandleFlush); + REGISTER_SYNC("FileHandle_sync", FileHandleSync); + REGISTER_SYNC("FileHandle_close", FileHandleClose); + #undef REGISTER_SYNC FilesystemManager::GetInstance().AddListener(this); } @@ -423,6 +472,161 @@ void write_file(const std::uint8_t* data, std::size_t len, std::string path, lon write_file(data, len, file); } +#define FIRST_BIT_MASK 0x80 +#define SECOND_BIT_MASK 0x40 +#define THIRD_BIT_MASK 0x20 +#define FOURTH_BIT_MASK 0x10 +#define FIFTH_BIT_MASK 0x08 + +enum class ByteOfUTF8Classification { + NOT_VALID, + ONE_BYTE_CHAR, + FIRST_OF_2_BYTES, + FIRST_OF_3_BYTES, + FIRST_OF_4_BYTES, + EXTENSION_BYTE +}; + +ByteOfUTF8Classification is_utf8_byte(uint8_t byte) { + if (FIRST_BIT_MASK & byte) { + if (SECOND_BIT_MASK & byte) { + if (THIRD_BIT_MASK & byte) { + if (FOURTH_BIT_MASK & byte) { + if (FIFTH_BIT_MASK & byte) { + return ByteOfUTF8Classification::NOT_VALID; + } else { + return ByteOfUTF8Classification::FIRST_OF_4_BYTES; + } + } else { + return ByteOfUTF8Classification::FIRST_OF_3_BYTES; + } + } else { + return ByteOfUTF8Classification::FIRST_OF_2_BYTES; + } + } else { + return ByteOfUTF8Classification::EXTENSION_BYTE; + } + } else { + return ByteOfUTF8Classification::ONE_BYTE_CHAR; + } +} + +void skip_partial_character(std::vector& buf) { + auto buf_position = buf.begin(); + + while (buf.end() != buf_position && + (ByteOfUTF8Classification::EXTENSION_BYTE == is_utf8_byte(*buf_position))) { + buf_position = buf.erase(buf_position); + LoggerD("Removed UTF-8 Extension Byte from begining of read buffer"); + } +} + +bool validate_and_check_character_count(std::vector& buf, unsigned long& char_count, + short& no_of_extensions_expected) { + ScopeLogger(); + char_count = 0; + no_of_extensions_expected = 0; + auto buf_position = buf.begin(); + + skip_partial_character(buf); + + while (buf.end() != buf_position) { + switch (is_utf8_byte(*buf_position)) { + case ByteOfUTF8Classification::EXTENSION_BYTE: + no_of_extensions_expected--; + if (0 > no_of_extensions_expected) { + return false; + } else if (0 == no_of_extensions_expected) { + char_count++; + } + break; + case ByteOfUTF8Classification::ONE_BYTE_CHAR: + if (0 != no_of_extensions_expected) { + return false; + } + char_count++; + break; + case ByteOfUTF8Classification::FIRST_OF_2_BYTES: + if (0 != no_of_extensions_expected) { + return false; + } + no_of_extensions_expected = 1; + break; + case ByteOfUTF8Classification::FIRST_OF_3_BYTES: + if (0 != no_of_extensions_expected) { + return false; + } + no_of_extensions_expected = 2; + break; + case ByteOfUTF8Classification::FIRST_OF_4_BYTES: + if (0 != no_of_extensions_expected) { + return false; + } + no_of_extensions_expected = 3; + break; + case ByteOfUTF8Classification::NOT_VALID: + return false; + default: + LoggerE("Abnormal value returned from is_utf8_byte function"); + } + buf_position++; + } + return true; +} + +/* + * add_utf8_chars_to_buffer + * Method returns false if byte read is not a valid utf8 char + */ +bool add_utf8_chars_to_buffer(FILE* file, std::vector& buf, int chars_to_read, + short& extension_bytes_expected) { + int character; + LoggerD("chars_to_read: %i", chars_to_read); + LoggerD("extension_bytes_expected: %i", extension_bytes_expected); + while (chars_to_read) { + if (extension_bytes_expected) { + character = getc(file); + if (EOF == character) { + return false; + } + buf.push_back(character); + if (!--extension_bytes_expected) { + chars_to_read--; + } + continue; + } + character = getc(file); + if(EOF == character) { + return false; + } + buf.push_back(character); + switch (is_utf8_byte(character)) { + case ByteOfUTF8Classification::ONE_BYTE_CHAR: + chars_to_read--; + break; + case ByteOfUTF8Classification::FIRST_OF_2_BYTES: + extension_bytes_expected = 1; + break; + case ByteOfUTF8Classification::FIRST_OF_3_BYTES: + extension_bytes_expected = 2; + break; + case ByteOfUTF8Classification::FIRST_OF_4_BYTES: + extension_bytes_expected = 3; + break; + case ByteOfUTF8Classification::EXTENSION_BYTE: + LoggerD("unexpected EXTENSION_BYTE"); + return false; + case ByteOfUTF8Classification::NOT_VALID: + LoggerE("unexpected NOT_VALID byte while reading utf-8"); + return false; + default: + LoggerE("Abnormal. Last read char: %c: ", character); + return false; + } + } + return true; +} + namespace base64 { static const char int_to_char[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; @@ -1018,6 +1222,87 @@ void FilesystemInstance::FileSystemManagerGetCanonicalPath(const picojson::value FilesystemManager::GetInstance().GetCanonicalPath(path, onSuccess, onError); } + +namespace { + +FILE* OpenFile(const std::string& path, const std::string& fopen_mode) { + FILE* file = std::fopen(path.c_str(), fopen_mode.c_str()); + if (!file) { + throw std::system_error{errno, std::generic_category(), "Could not open file"}; + } + + return file; +} + +FILE* MakeParentsAndOpenFile(const std::string& path, const std::string& fopen_mode) { + /* + * If fopen fails, created parent directories have to be removed. + * Save the path to the first nonexistent parent directory in the file path, + * to know where to start recursive removal + */ + std::string first_nonexistent_parent = FilesystemUtils::Dirname(path); + struct ::stat buf; + + while (!FilesystemUtils::CheckIfExists(FilesystemUtils::Dirname(first_nonexistent_parent), + &buf)) { + first_nonexistent_parent = FilesystemUtils::Dirname(first_nonexistent_parent); + } + + FilesystemUtils::Mkdir(FilesystemUtils::Dirname(path), true); + + FILE* file = std::fopen(path.c_str(), fopen_mode.c_str()); + if (file) { + return file; + } + + std::system_error fopen_error = std::system_error(errno, std::generic_category(), "Could not open file"); + + try { + FilesystemUtils::RemoveDirectoryRecursively(first_nonexistent_parent); + } catch (const std::system_error& error) { + LoggerD("Failed to remove created parent directories: %s", error.what()); + } + + throw fopen_error; +} + +} //namespace + +void FilesystemInstance::FileSystemManagerOpenFile(const picojson::value& args, + picojson::object& out) { + ScopeLogger(); + const int unique_id = static_cast(args.get("id").get()); + if (opened_files.find(unique_id) != opened_files.end()) { + LogAndReportError(IOException("Internal error (id is already in use)"), out); + return; + } + + if (WriteAccessRequested(args)) { + CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemWrite, &out); + } + + if (ReadAccessRequested(args)) { + CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemRead, &out); + } + + const std::string& path = args.get("path").get(); + const std::string open_mode = GetFopenMode(args); + FILE* file = nullptr; + try { + if (ShouldMakeParents(args)) { + file = MakeParentsAndOpenFile(path, open_mode); + } else { + file = OpenFile(path, open_mode); + } + } catch (const std::system_error& error) { + FilesystemUtils::TranslateException(error, out); + return; + } + + opened_files.emplace(std::make_pair(unique_id, std::make_shared(file))); + ReportSuccess(out); +} + void FilesystemInstance::FileSystemManagerCreateDirectory(const picojson::value& args, picojson::object& out) { ScopeLogger(); @@ -1502,6 +1787,543 @@ void FilesystemInstance::FileSystemManagerGetLimits(const picojson::value& args, ReportSuccess(response, out); } +void FilesystemInstance::FileHandleSeek(const picojson::value& args, picojson::object& out) { + ScopeLogger(); + const int fh_id = static_cast(args.get("id").get()); + const long offset = static_cast(args.get("offset").get()); + const std::string& _whence = args.get("whence").get(); + + auto fh = opened_files.find(fh_id); + + int whence = SEEK_SET; + + if ("CURRENT" == _whence) { + whence = SEEK_CUR; + LoggerD("SEEK_CUR selected"); + } else if ("END" == _whence) { + whence = SEEK_END; + LoggerD("SEEK_END selected"); + } + + if (opened_files.end() == fh) { + LogAndReportError(IOException("Invalid FileHandle"), out); + return; + } + + auto handle = fh->second; + + auto logic = [handle, whence, offset](decltype(out) out) { + try { + long ret = fseek(handle->file_handle, offset, whence); + if (0 != ret) { + LoggerE("fseek returned failed"); + int errsv = errno; + std::string error_message = + std::string("seek failed, fileHandle may be corrupted, error message: ") + + strerror(errsv); + LogAndReportError(IOException(error_message.c_str()), out); + return; + } + + ret = ftell(handle->file_handle); + if (-1L == ret) { + LoggerE("ftell returned failed"); + int errsv = errno; + std::string error_message = + std::string("seek failed, fileHandle may be corrupted, error message: ") + + strerror(errsv); + LogAndReportError(IOException(error_message.c_str()), out); + return; + } + + ReportSuccess(picojson::value((double)ret), out); + } catch (std::runtime_error& e) { + LoggerE("Cannot read, cause: %s", e.what()); + LogAndReportError(IOException(e.what()), out); + return; + } + }; + + bool blocking = args.contains("blocking") ? args.get("blocking").get() : true; + + if (blocking) { + logic(out); + return; + } else { + // Async logic + double callback_id = args.get("callbackId").get(); + this->worker.add_job([this, callback_id, logic] { + picojson::value response = picojson::value(picojson::object()); + picojson::object& async_out = response.get(); + async_out["callbackId"] = picojson::value(callback_id); + logic(async_out); + this->PostMessage(response.serialize().c_str()); + }); + + // Sync return + ReportSuccess(out); + return; + } +} + +void FilesystemInstance::FileHandleReadString(const picojson::value& args, picojson::object& out) { + ScopeLogger(); + CHECK_EXIST(args, "id", out) + const int fh_id = static_cast(args.get("id").get()); + const std::string& encoding = + args.contains("encoding") ? args.get("encoding").get() : "utf-8"; + + auto fh = opened_files.find(fh_id); + + if (opened_files.end() == fh) { + LogAndReportError(IOException("Invalid FileHandle"), out); + return; + } + + size_t count; + bool whole_file = false; + if (args.contains("count")) { + count = static_cast(args.get("count").get()); + } else { + try { + count = file_size(fh->second->file_handle); + whole_file = true; + } catch (const std::system_error& e) { + LogAndReportError(IOException(e.what()), out); + return; + } + } + LoggerD("count: %d", count); + + if (std::string::npos == count) { + LogAndReportError(InvalidValuesException("Invalid count was given"), out); + return; + } + + auto handle = fh->second; + + auto logic = [handle, count, encoding, whole_file](decltype(out) out) { + try { + std::vector buf = read_file(handle->file_handle, count); + if (encoding == "iso-8859-1") { // for iso-8859-1 1 byte is equal to 1 character + out["result"] = picojson::value(picojson::string_type, true); + latin1::to_utf8(buf, out["result"].get()); + ReportSuccess(out); + } else if (encoding == "utf-8") { + unsigned long char_count; + short expected_extension_bytes; + if (!validate_and_check_character_count(buf, char_count, expected_extension_bytes)) { + LogAndReportError( + IOException("File doesn't contain UTF-8 encoded string with given length"), out); + return; + } + LoggerD("char_count: %ld", char_count); + LoggerD("ftell: %i", ftell(handle->file_handle)); + if (!(std::feof( + handle->file_handle))) { // read number of characters if not whole file read + LoggerD("count parameter given: %d", count); + if (!whole_file && !add_utf8_chars_to_buffer(handle->file_handle, buf, count - char_count, + expected_extension_bytes)) { + LogAndReportError( + IOException("File doesn't contain UTF-8 encoded string with given length"), out); + } + } + buf.push_back('\0'); + const char* str = (const char*)buf.data(); + ReportSuccess(picojson::value{str}, out); + } else { + LogAndReportError(NotSupportedException("Given encoding is not supported."), out); + return; + } + } catch (std::runtime_error& e) { + LoggerE("Cannot read, cause: %s", e.what()); + LogAndReportError(IOException(e.what()), out); + return; + } + }; + + bool blocking = args.contains("blocking") ? args.get("blocking").get() : true; + + if (blocking) { + logic(out); + return; + } else { + // Async logic + double callback_id = args.get("callbackId").get(); + this->worker.add_job([this, callback_id, logic] { + picojson::value response = picojson::value(picojson::object()); + picojson::object& async_out = response.get(); + async_out["callbackId"] = picojson::value(callback_id); + logic(async_out); + this->PostMessage(response.serialize().c_str()); + }); + + // Sync return + ReportSuccess(out); + return; + } +} + +void FilesystemInstance::FileHandleWriteString(const picojson::value& args, picojson::object& out) { + ScopeLogger(); + CHECK_EXIST(args, "id", out) + CHECK_EXIST(args, "string", out) + const int fh_id = static_cast(args.get("id").get()); + const std::string& str = args.get("string").get(); + const std::string& encoding = + args.contains("encoding") ? args.get("encoding").get() : "utf-8"; + + auto fh = opened_files.find(fh_id); + + if (opened_files.end() == fh) { + LogAndReportError(IOException("Invalid FileHandle"), out); + return; + } + + auto handle = fh->second; + + auto logic = [str, handle, encoding](decltype(out) out) { + try { + std::vector data; + data.resize(str.size()); + + if (encoding == "iso-8859-1") { + latin1::from_utf8(str, data); + } else { + LoggerD("copying string memory to vector"); + std::memcpy(data.data(), str.data(), str.size()); + } + write_file(data.data(), data.size(), handle->file_handle); + ReportSuccess(picojson::value{(double)data.size()}, out); + } catch (std::runtime_error& e) { + LoggerE("Cannot write, cause: %s", e.what()); + LogAndReportError(IOException(e.what()), out); + return; + } + }; + + bool blocking = args.contains("blocking") ? args.get("blocking").get() : true; + + if (blocking) { + logic(out); + return; + } else { + // Async logic + double callback_id = args.get("callbackId").get(); + this->worker.add_job([this, callback_id, logic] { + picojson::value response = picojson::value(picojson::object()); + picojson::object& async_out = response.get(); + async_out["callbackId"] = picojson::value(callback_id); + logic(async_out); + this->PostMessage(response.serialize().c_str()); + }); + + // Sync return + ReportSuccess(out); + return; + } +} + +void FilesystemInstance::FileHandleReadData(const picojson::value& args, picojson::object& out) { + ScopeLogger(); + CHECK_EXIST(args, "id", out) + const int fh_id = static_cast(args.get("id").get()); + auto fh = opened_files.find(fh_id); + + if (opened_files.end() == fh) { + LoggerE("FileHandle with id: %d not found", fh_id); + LogAndReportError(IOException("Invalid FileHandle"), out); + return; + } + + size_t size; + if (args.contains("size")) { + size = static_cast(args.get("size").get()); + } else { + try { + size = file_size(fh->second->file_handle); + } catch (const std::system_error& e) { + LogAndReportError(IOException(e.what()), out); + return; + } + } + LoggerD("size: %d", size); + + if (std::string::npos == size) { + LogAndReportError(InvalidValuesException("Invalid size was given"), out); + return; + } + + auto handle = fh->second; + + auto logic = [handle, size](decltype(out) out) { + try { + std::vector data = read_file(handle->file_handle, size); + out["result"] = picojson::value(picojson::string_type, true); + encode_binary_in_string(data, out["result"].get()); + } catch (std::runtime_error& e) { + LoggerE("Cannot read, cause: %s", e.what()); + LogAndReportError(IOException(e.what()), out); + return; + } + }; + + bool blocking = args.contains("blocking") ? args.get("blocking").get() : true; + + if (blocking) { + logic(out); + return; + } else { + // Async logic + double callback_id = args.get("callbackId").get(); + this->worker.add_job([this, callback_id, logic] { + picojson::value response = picojson::value(picojson::object()); + picojson::object& async_out = response.get(); + async_out["callbackId"] = picojson::value(callback_id); + logic(async_out); + this->PostMessage(response.serialize().c_str()); + }); + + // Sync return + ReportSuccess(out); + return; + } +} + +void FilesystemInstance::FileHandleWriteData(const picojson::value& args, picojson::object& out) { + ScopeLogger(); + CHECK_EXIST(args, "id", out) + CHECK_EXIST(args, "data", out) + const auto& str = args.get("data").get(); + const int fh_id = static_cast(args.get("id").get()); + + auto fh = opened_files.find(fh_id); + if (opened_files.end() == fh) { + LoggerE("FileHandle with id: %d not found", fh_id); + LogAndReportError(IOException("Invalid FileHandle"), out); + return; + } + + auto handle = fh->second; + + auto logic = [str, handle](decltype(out) out) { + try { + std::vector bytes; + decode_binary_from_string(str, bytes); + write_file(bytes.data(), bytes.size(), handle->file_handle); + } catch (std::runtime_error& e) { + LogAndReportError(IOException(e.what()), out); + return; + } + }; + + bool blocking = args.contains("blocking") ? args.get("blocking").get() : true; + + if (blocking) { + logic(out); + return; + } else { + // Async logic + double callback_id = args.get("callbackId").get(); + this->worker.add_job([this, callback_id, logic] { + picojson::value response = picojson::value(picojson::object()); + picojson::object& async_out = response.get(); + async_out["callbackId"] = picojson::value(callback_id); + logic(async_out); + this->PostMessage(response.serialize().c_str()); + }); + + // Sync return + ReportSuccess(out); + return; + } +} + +void FilesystemInstance::FileHandleFlush(const picojson::value& args, picojson::object& out) { + ScopeLogger(); + const int fh_id = static_cast(args.get("id").get()); + + auto fh = opened_files.find(fh_id); + if (opened_files.end() == fh) { + LogAndReportError(IOException("Invalid FileHandle"), out); + return; + } + + auto handle = fh->second; + + auto logic = [handle](decltype(out) out) { + try { + int ret = fflush(handle->file_handle); + if (ret) { + int errsv = errno; + std::string error_message = + std::string("flush failed, error message: ") + strerror(errsv); + LogAndReportError(IOException(error_message.c_str()), out); + return; + } + } catch (std::runtime_error& e) { + LogAndReportError(IOException(e.what()), out); + return; + } + }; + + bool blocking = args.contains("blocking") ? args.get("blocking").get() : true; + + if (blocking) { + logic(out); + return; + } else { + // Async logic + double callback_id = args.get("callbackId").get(); + this->worker.add_job([this, callback_id, logic] { + picojson::value response = picojson::value(picojson::object()); + picojson::object& async_out = response.get(); + async_out["callbackId"] = picojson::value(callback_id); + logic(async_out); + this->PostMessage(response.serialize().c_str()); + }); + + // Sync return + ReportSuccess(out); + return; + } +} + +void FilesystemInstance::FileHandleSync(const picojson::value& args, picojson::object& out) { + ScopeLogger(); + const int fh_id = static_cast(args.get("id").get()); + + auto fh = opened_files.find(fh_id); + if (opened_files.end() == fh) { + LogAndReportError(IOException("Invalid FileHandle"), out); + return; + } + + auto handle = fh->second; + + auto logic = [handle](decltype(out) out) { + try { + int ret = fsync(fileno(handle->file_handle)); + if (ret) { + int errsv = errno; + std::string error_message = + std::string("sync failed, error message: ") + strerror(errsv); + LogAndReportError(IOException(error_message.c_str()), out); + return; + } + } catch (std::runtime_error& e) { + LogAndReportError(IOException(e.what()), out); + return; + } + }; + + bool blocking = args.contains("blocking") ? args.get("blocking").get() : true; + + if (blocking) { + logic(out); + return; + } else { + // Async logic + double callback_id = args.get("callbackId").get(); + this->worker.add_job([this, callback_id, logic] { + picojson::value response = picojson::value(picojson::object()); + picojson::object& async_out = response.get(); + async_out["callbackId"] = picojson::value(callback_id); + logic(async_out); + this->PostMessage(response.serialize().c_str()); + }); + + // Sync return + ReportSuccess(out); + return; + } +} + +void FilesystemInstance::FileHandleClose(const picojson::value& args, picojson::object& out) { + ScopeLogger(); + const int fh_id = static_cast(args.get("id").get()); + auto fh = opened_files.find(fh_id); + if (opened_files.end() == fh) { + LogAndReportError(IOException("Invalid FileHandle"), out); + return; + } + + std::shared_ptr handle = fh->second; + opened_files.erase(fh); + + auto logic = [handle](decltype(out) out) { + try { + if (!handle->file_handle) { + LogAndReportError(IOException("File handle already closed."), out); + return; + } + int ret = fclose(handle->file_handle); + handle->file_handle = nullptr; + if (ret) { + int errsv = errno; + std::string error_message = + std::string("close failed, error message: ") + strerror(errsv); + LogAndReportError(IOException(error_message.c_str()), out); + return; + } + } catch (std::runtime_error& e) { + LogAndReportError(IOException(e.what()), out); + return; + } + }; + + bool blocking = args.contains("blocking") ? args.get("blocking").get() : true; + + if (blocking) { + bool ready = false; + bool done = false; + std::mutex mutex; + std::condition_variable conditional_variable; + // adding empty job to worker's queue, in order to wait for all jobs to be done before closing + // FILE* + this->worker.add_job([] {}, + [&conditional_variable, &mutex, &ready, &done, logic, &out] { + { + // wait for close + std::unique_lock lock(mutex); + conditional_variable.wait(lock, [&ready] { return ready; }); + + logic(out); + done = true; + } + conditional_variable.notify_one(); + }); + + { + // let know that close is ready + std::unique_lock lock(mutex); + ready = true; + } + conditional_variable.notify_one(); + + { + // wait for worker + std::unique_lock lock(mutex); + conditional_variable.wait(lock, [&done] { return done; }); + } + handle->file_handle = nullptr; + } else { + // Async logic + double callback_id = args.get("callbackId").get(); + this->worker.add_job([this, callback_id, logic] { + picojson::value response = picojson::value(picojson::object()); + picojson::object& async_out = response.get(); + async_out["callbackId"] = picojson::value(callback_id); + logic(async_out); + this->PostMessage(response.serialize().c_str()); + }); + + // Sync return + ReportSuccess(out); + return; + } +} + #undef CHECK_EXIST } // namespace filesystem diff --git a/src/filesystem/filesystem_instance.h b/src/filesystem/filesystem_instance.h index dbe9ee7..0fdb833 100644 --- a/src/filesystem/filesystem_instance.h +++ b/src/filesystem/filesystem_instance.h @@ -50,7 +50,6 @@ class FileHandle { FileHandle& operator=(const FileHandle&) = delete; FileHandle& operator=(FileHandle&&) = delete; - private: FILE* file_handle; }; @@ -65,7 +64,7 @@ class FilesystemInstance : public common::ParsedInstance, FilesystemStateChangeL /** * @brief Implements single worker executing in new thread * - * Jobs are done in order. If this worker is destroyed all pending jobs are cancelled, + * Jobs are done in FIFO order. If this worker is destroyed all pending jobs are cancelled, * and all remaining 'finally' functions are called. */ class Worker { @@ -130,6 +129,7 @@ class FilesystemInstance : public common::ParsedInstance, FilesystemStateChangeL void PrepareError(const FilesystemError& error, picojson::object& out); void FileSystemManagerGetCanonicalPath(const picojson::value& args, picojson::object& out); + void FileSystemManagerOpenFile(const picojson::value& args, picojson::object& out); void FileSystemManagerCreateDirectory(const picojson::value& args, picojson::object& out); void FileSystemManagerDeleteFile(const picojson::value& args, picojson::object& out); void FileSystemManagerDeleteDirectory(const picojson::value& args, picojson::object& out); @@ -144,6 +144,54 @@ class FilesystemInstance : public common::ParsedInstance, FilesystemStateChangeL void FileSystemManagerPathExists(const picojson::value& args, picojson::object& out); void FileSystemManagerToURI(const picojson::value& args, picojson::object& out); void FileSystemManagerGetLimits(const picojson::value& args, picojson::object& out); + + /** + * @brief wrapper for std::fseek function. + * Sets the file position indicator for the file stream stream. + * @parameter out has always set status, either success or error. + */ + void FileHandleSeek(const picojson::value& args, picojson::object& out); + + /** + * @brief Reads file contents as string. + * @parameter out has always set status, either success or error. + * In case of success, string value is passed. + */ + void FileHandleReadString(const picojson::value& args, picojson::object& out); + + /** + * @brief Writes string to file. + * @parameter out has always set status, either success or error. + */ + void FileHandleWriteString(const picojson::value& args, picojson::object& out); + + /** + * @brief Reads file contents as binary data, can use worker and not block GUI. + * @parameter out has always set status, either success or error. + * In case of success, encoded uint8_t array is passed. + */ + void FileHandleReadData(const picojson::value& args, picojson::object& out); + + /** + * @brief Writes binary data to file, can use worker and not block GUI. + * @parameter out has always set status, either success or error. + */ + void FileHandleWriteData(const picojson::value& args, picojson::object& out); + + /** + * @brief wrapper for std::fflush function. + * Writes any unwritten data from the stream's buffer to the associated output device. + * @parameter out has always set status, either success or error. + */ + void FileHandleFlush(const picojson::value& args, picojson::object& out); + void FileHandleSync(const picojson::value& args, picojson::object& out); + + /** + * @brief wrapper for std::fclose function. + * Closes the given file stream. + * @parameter out has always set status, either success or error. + */ + void FileHandleClose(const picojson::value& args, picojson::object& out); }; } // namespace filesystem -- 2.7.4 From fd1d402573e83de459363986584debe84b5ff873 Mon Sep 17 00:00:00 2001 From: Arkadiusz Pietraszek Date: Thu, 19 Apr 2018 13:10:50 +0200 Subject: [PATCH 13/16] [Filesystem] Added filesystem methods to file_system_manager.js Change-Id: I5a6f55d9dc82a98ad0e1e4e6d062ccc93e40479d Signed-off-by: Szymon Jastrzebski Signed-off-by: Arkadiusz Pietraszek Signed-off-by: Jakub Skowron Signed-off-by: Pawel Wasowski --- src/filesystem/js/file_system_manager.js | 577 +++++++++++++++++++++++++++++-- 1 file changed, 539 insertions(+), 38 deletions(-) mode change 100755 => 100644 src/filesystem/js/file_system_manager.js diff --git a/src/filesystem/js/file_system_manager.js b/src/filesystem/js/file_system_manager.js old mode 100755 new mode 100644 index 622f38d..b40df95 --- a/src/filesystem/js/file_system_manager.js +++ b/src/filesystem/js/file_system_manager.js @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - + function FileSystemStorage(data) { Object.defineProperties(this, { label: {value: data.label, writable: false, enumerable: true}, @@ -22,33 +22,534 @@ function FileSystemStorage(data) { }); } -var PATH_MAX = 4096; + +var FileStreamManager = function() { + this.nextId = 0; +}; + +FileStreamManager.prototype.getNextFileHandleId = function() { + return ++this.nextId; +}; + +var fileStreamManager = new FileStreamManager(); function FileSystemManager() { + var limits = native_.getResultObject(native_.callSync('FileSystemManager_getLimits')); Object.defineProperties(this, { - maxPathLength: {value: PATH_MAX, writable: false, enumerable: true} + maxNameLength: {value: limits[0], writable: false, enumerable: true}, + maxPathLength: {value: limits[1], writable: false, enumerable: true} }); } +FileSystemManager.prototype.openFile = function() { + var args = validator_.validateArgs(arguments, [ + {name: 'path', type: types_.STRING}, + {name: 'openMode', type: types_.ENUM, values: type_.getValues(FileMode)}, + {name: 'makeParents', type: types_.BOOLEAN, optional: true} + ]); + + if (!args.has.makeParents) { + args.makeParents = true; + } + + var data = { + path: commonFS_.toRealPath(args.path), + openMode: args.openMode, + makeParents: args.makeParents, + id: fileStreamManager.getNextFileHandleId() + }; + + if (!data.path) { + throw new WebAPIException( + WebAPIException.NOT_FOUND_ERR, 'Invalid path: ' + args.path); + } + + var result = native_.callSync('FileSystemManager_openFile', data); + if (native_.isFailure(result)) { + throw native_.getErrorObject(result); + } else { + return new FileHandle(data.id, args.path, args.openMode); + } +}; + +FileSystemManager.prototype.createDirectory = function() { + var args = validator_.validateArgs(arguments, [ + {name: 'path', type: types_.STRING}, + {name: 'makeParents', type: types_.BOOLEAN, optional: true}, + {name: 'successCallback', type: types_.FUNCTION, optional: true, nullable: true}, + {name: 'errorCallback', type: types_.FUNCTION, optional: true, nullable: true} + ]); + + if (!args.has.makeParents) { + args.makeParents = true; + } + + var data = {path: commonFS_.toRealPath(args.path), makeParents: args.makeParents}; + + if (!data.path) { + throw new WebAPIException( + WebAPIException.NOT_FOUND_ERR, 'Invalid path: ' + args.path); + } + + var callback = function(result) { + if (native_.isFailure(result)) { + native_.callIfPossible(args.errorCallback, native_.getErrorObject(result)); + } else { + native_.callIfPossible(args.successCallback); + } + }; + + var result = native_.call('FileSystemManager_createDirectory', data, callback); + if (native_.isFailure(result)) { + throw native_.getErrorObject(result); + } +}; + +FileSystemManager.prototype.deleteFile = function() { + var args = validator_.validateArgs(arguments, [ + {name: 'path', type: types_.STRING}, + {name: 'successCallback', type: types_.FUNCTION, optional: true, nullable: true}, + {name: 'errorCallback', type: types_.FUNCTION, optional: true, nullable: true} + ]); + + var data = {path: commonFS_.toRealPath(args.path)}; + + if (!data.path) { + throw new WebAPIException( + WebAPIException.NOT_FOUND_ERR, 'Invalid path: ' + args.path); + } + + var callback = function(result) { + if (native_.isFailure(result)) { + native_.callIfPossible(args.errorCallback, native_.getErrorObject(result)); + } else { + native_.callIfPossible(args.successCallback); + } + }; + + var result = native_.call('FileSystemManager_deleteFile', data, callback); + if (native_.isFailure(result)) { + throw native_.getErrorObject(result); + } +}; + +FileSystemManager.prototype.deleteDirectory = function() { + var args = validator_.validateArgs(arguments, [ + {name: 'path', type: types_.STRING}, + {name: 'recursive', type: types_.BOOLEAN, optional: true}, + {name: 'successCallback', type: types_.FUNCTION, optional: true, nullable: true}, + {name: 'errorCallback', type: types_.FUNCTION, optional: true, nullable: true} + ]); + + if (!args.has.recursive) { + args.recursive = true; + } + + var realPath = commonFS_.toRealPath(args.path); + if (!realPath) { + throw new WebAPIException(WebAPIException.INVALID_VALUES_ERR, 'Invalid path.'); + } + + var data = {path: realPath, recursive: args.recursive}; + + var callback = function(result) { + if (native_.isFailure(result)) { + native_.callIfPossible(args.errorCallback, native_.getErrorObject(result)); + } else { + native_.callIfPossible(args.successCallback); + } + }; + + var result = native_.call('FileSystemManager_deleteDirectory', data, callback); + if (native_.isFailure(result)) { + throw native_.getErrorObject(result); + } +}; + +FileSystemManager.prototype.copyFile = function() { + var args = validator_.validateArgs(arguments, [ + {name: 'path', type: types_.STRING}, {name: 'destinationPath', type: types_.STRING}, + {name: 'overwrite', type: types_.BOOLEAN, optional: true}, + {name: 'successCallback', type: types_.FUNCTION, optional: true, nullable: true}, + {name: 'errorCallback', type: types_.FUNCTION, optional: true, nullable: true} + ]); + + if (!args.has.overwrite) { + args.overwrite = false; + } + + var data = { + path: commonFS_.toRealPath(args.path), + destinationPath: commonFS_.toRealPath(args.destinationPath), + overwrite: args.overwrite + }; + + if (!data.path) { + throw new WebAPIException( + WebAPIException.INVALID_VALUES_ERR, 'Invalid path: ' + args.path); + } + if (!data.destinationPath) { + throw new WebAPIException( + WebAPIException.INVALID_VALUES_ERR, 'Invalid path: ' + args.destinationPath); + } + + var callback = function(result) { + if (native_.isFailure(result)) { + native_.callIfPossible(args.errorCallback, native_.getErrorObject(result)); + } else { + native_.callIfPossible(args.successCallback); + } + }; + + var result = native_.call('FileSystemManager_copyFile', data, callback); + if (native_.isFailure(result)) { + throw native_.getErrorObject(result); + } +}; + +FileSystemManager.prototype.copyDirectory = function() { + var args = validator_.validateArgs(arguments, [ + {name: 'path', type: types_.STRING}, + {name: 'destinationPath', type: types_.STRING}, + {name: 'overwrite', type: types_.BOOLEAN, optional: true}, + {name: 'successCallback', type: types_.FUNCTION, optional: true, nullable: true}, + {name: 'errorCallback', type: types_.FUNCTION, optional: true, nullable: true} + ]); + + var realPath = commonFS_.toRealPath(args.path); + var realDestinationPath = commonFS_.toRealPath(args.destinationPath); + if (!realPath || !realDestinationPath) { + throw new WebAPIException(WebAPIException.INVALID_VALUES_ERR, 'Invalid path.'); + } + + if (!args.has.overwrite) { + args.overwrite = false; + } + + var data = { + path: realPath, + destinationPath: realDestinationPath, + overwrite: args.overwrite + }; + + var callback = function(result) { + if (native_.isFailure(result)) { + native_.callIfPossible(args.errorCallback, native_.getErrorObject(result)); + } else { + native_.callIfPossible(args.successCallback); + } + }; + + var result = native_.call('FileSystemManager_copyDirectory', data, callback); + if (native_.isFailure(result)) { + throw native_.getErrorObject(result); + } +}; + +FileSystemManager.prototype.moveFile = function() { + var args = validator_.validateArgs(arguments, [ + {name: 'path', type: types_.STRING}, + {name: 'destinationPath', type: types_.STRING}, + {name: 'overwrite', type: types_.BOOLEAN, optional: true}, + {name: 'successCallback', type: types_.FUNCTION, optional: true, nullable: true}, + {name: 'errorCallback', type: types_.FUNCTION, optional: true, nullable: true} + ]); + + var realPath = commonFS_.toRealPath(args.path); + var realDestinationPath = commonFS_.toRealPath(args.destinationPath); + if (!realPath || !realDestinationPath) { + throw new WebAPIException(WebAPIException.INVALID_VALUES_ERR, 'Invalid path.'); + } + + if (!args.has.overwrite) { + args.overwrite = false; + } + + var data = { + path: realPath, + destinationPath: realDestinationPath, + overwrite: args.overwrite + }; + + var callback = function(result) { + if (native_.isFailure(result)) { + native_.callIfPossible(args.errorCallback, native_.getErrorObject(result)); + } else { + native_.callIfPossible(args.successCallback); + } + }; + + var result = native_.call('FileSystemManager_moveFile', data, callback); + if (native_.isFailure(result)) { + throw native_.getErrorObject(result); + } +}; + +FileSystemManager.prototype.moveDirectory = function() { + var args = validator_.validateArgs(arguments, [ + {name: 'path', type: types_.STRING}, + {name: 'destinationPath', type: types_.STRING}, + {name: 'overwrite', type: types_.BOOLEAN, optional: true}, + {name: 'successCallback', type: types_.FUNCTION, optional: true, nullable: true}, + {name: 'errorCallback', type: types_.FUNCTION, optional: true, nullable: true} + ]); + + var realPath = commonFS_.toRealPath(args.path); + var realDestinationPath = commonFS_.toRealPath(args.destinationPath); + if (!realPath || !realDestinationPath) { + throw new WebAPIException(WebAPIException.INVALID_VALUES_ERR, 'Invalid path.'); + } + + if (!args.has.overwrite) { + args.overwrite = false; + } + + var data = { + path: realPath, + destinationPath: realDestinationPath, + overwrite: args.overwrite + }; + + var callback = function(result) { + if (native_.isFailure(result)) { + native_.callIfPossible(args.errorCallback, native_.getErrorObject(result)); + } else { + native_.callIfPossible(args.successCallback); + } + }; + + var result = native_.call('FileSystemManager_moveDirectory', data, callback); + if (native_.isFailure(result)) { + throw native_.getErrorObject(result); + } +}; + +FileSystemManager.prototype.rename = function() { + var args = validator_.validateArgs(arguments, [ + {name: 'path', type: types_.STRING}, + {name: 'newName', type: types_.STRING}, + {name: 'successCallback', type: types_.FUNCTION, optional: true, nullable: true}, + {name: 'errorCallback', type: types_.FUNCTION, optional: true, nullable: true} + ]); + + if ((-1 !== args.newName.indexOf('/')) || (-1 !== args.newName.indexOf('\x00'))) { + throw new WebAPIException( + WebAPIException.INVALID_VALUES_ERR, 'newName contains invalid character.'); + } + + var realPath = commonFS_.toRealPath(args.path); + if (!realPath) { + throw new WebAPIException(WebAPIException.INVALID_VALUES_ERR, 'Invalid path.'); + } + + var data = {path: realPath, newName: args.newName}; + + var callback = function(result) { + if (native_.isFailure(result)) { + native_.callIfPossible(args.errorCallback, native_.getErrorObject(result)); + } else { + native_.callIfPossible(args.successCallback); + } + }; + + var result = native_.call('FileSystemManager_rename', data, callback); + if (native_.isFailure(result)) { + throw native_.getErrorObject(result); + } +}; + +function throwIfNotDate(argument, name) { + if (argument instanceof Date) { + return true; + } + throw new WebAPIException( + WebAPIException.TYPE_MISMATCH_ERR, + 'Argument "' + name + '" in a filter is not of type Date.'); +} + +FileSystemManager.prototype.listDirectory = function() { + var args = validator_.validateArgs(arguments, [ + {name: 'path', type: types_.STRING}, + {name: 'successCallback', type: types_.FUNCTION, optional: true}, + {name: 'errorCallback', type: types_.FUNCTION, optional: true, nullable: true}, + {name: 'filter', type: types_.DICTIONARY, optional: true, nullable: true} + ]); + + if (!args.has.filter) { + args.filter = {}; + } + + if (args.filter.hasOwnProperty('startModified')) { + throwIfNotDate(args.filter.endModified, 'endModified'); + args.filter.startModified = args.filter.startModified.getTime() / 1000; + } + if (args.filter.hasOwnProperty('endModified')) { + throwIfNotDate(args.filter.endModified, 'endModified'); + args.filter.endModified = args.filter.endModified.getTime() / 1000; + } + if (args.filter.hasOwnProperty('startCreated')) { + throwIfNotDate(args.filter.endModified, 'endModified'); + args.filter.startCreated = args.filter.startCreated.getTime() / 1000; + } + if (args.filter.hasOwnProperty('endCreated')) { + throwIfNotDate(args.filter.endModified, 'endModified'); + args.filter.endCreated = args.filter.endCreated.getTime() / 1000; + } + + var data = {path: commonFS_.toRealPath(args.path), filter: args.filter}; + + if (!data.path) { + throw new WebAPIException( + WebAPIException.INVALID_VALUES_ERR, 'Invalid path: ' + args.path); + } + + var callback = function(result) { + if (native_.isFailure(result)) { + native_.callIfPossible(args.errorCallback, native_.getErrorObject(result)); + } else { + var names = native_.getResultObject(result); + if (args.filter.hasOwnProperty('name')) { + var regex_name = stringToRegex(args.filter.name); + for (var i = names.length - 1; i >= 0; i--) { + if (!regex_name.test(names[i])) { + names.splice(i, 1); + } + } + } + native_.callIfPossible(args.successCallback, names); + } + }; + + var result = native_.call('FileSystemManager_listDirectory', data, callback); + if (native_.isFailure(result)) { + throw native_.getErrorObject(result); + } +}; + +FileSystemManager.prototype.toURI = function() { + var args = validator_.validateArgs(arguments, [{name: 'path', type: types_.STRING}]); + + // The toRealPath function will convert any string to absolute path, if possible. + // The function returns undefined for path, which starts with not-existing virtual root. + var realPath = commonFS_.toRealPath(args.path); + + if (!realPath) { + throw new WebAPIException(WebAPIException.INVALID_VALUES_ERR, 'Invalid path.'); + } + + return 'file://' + realPath; +}; + +FileSystemManager.prototype.isFile = function() { + var args = validator_.validateArgs(arguments, [{name: 'path', type: types_.STRING}]); + + // The toRealPath function will convert any string to absolute path, if possible. + // The function returns undefined for path, which starts with not-existing virtual root. + var realPath = commonFS_.toRealPath(args.path); + + if (!realPath) { + throw new WebAPIException(WebAPIException.INVALID_VALUES_ERR, 'Invalid path.'); + } + + var data = {path: realPath}; + + var result = native_.callSync('FileSystemManager_isFile', data); + if (native_.isFailure(result)) { + throw native_.getErrorObject(result); + } else { + return native_.getResultObject(result); + } +}; + +FileSystemManager.prototype.isDirectory = function() { + var args = validator_.validateArgs(arguments, [{name: 'path', type: types_.STRING}]); + + // The toRealPath function will convert any string to absolute path, if possible. + // The function returns undefined for path, which starts with not-existing virtual root. + var realPath = commonFS_.toRealPath(args.path); + + if (!realPath) { + throw new WebAPIException(WebAPIException.INVALID_VALUES_ERR, 'Invalid path.'); + } + + var data = {path: realPath}; + + var result = native_.callSync('FileSystemManager_isDirectory', data); + if (native_.isFailure(result)) { + throw native_.getErrorObject(result); + } else { + return native_.getResultObject(result); + } +}; + +FileSystemManager.prototype.pathExists = function() { + var args = validator_.validateArgs(arguments, [{name: 'path', type: types_.STRING}]); + + // The toRealPath function will convert any string to absolute path, if possible. + // The function returns undefined for path, which starts with not-existing virtual root. + var realPath = commonFS_.toRealPath(args.path); + + if (!realPath) { + throw new WebAPIException(WebAPIException.INVALID_VALUES_ERR, 'Invalid path.'); + } + var data = {path: realPath}; + + var result = native_.callSync('FileSystemManager_pathExists', data); + if (native_.isFailure(result)) { + throw native_.getErrorObject(result); + } else { + return native_.getResultObject(result); + } +}; + +FileSystemManager.prototype.getDirName = function() { + var args = validator_.validateArgs(arguments, [{name: 'path', type: types_.STRING}]); + var path = args.path; + + path = mergeMultipleSlashes(path); + if (path.startsWith('file://')) { + path = path.substring('file://'.length - 1, path.length - 1); + } + + if (path.startsWith('/') && + 0 === path.lastIndexOf('/')) { // handle the "/" and "/file.ext" + return '/'; + } else if (path.endsWith('/')) { // cut the last '/' + path = path.substring(0, path.length - 1); + } + + var index = path.lastIndexOf('/'); + if (-1 !== index) { + path = path.substring(0, index); // cut the directory/file the path points to + } + return path; +}; + function resolve() { var args = validator_.validateArgs(arguments, [ - {name: 'location', type: types_.STRING}, - {name: 'onsuccess', type: types_.FUNCTION}, - {name: 'onerror', type: types_.FUNCTION, optional: true, nullable: true}, - {name: 'mode', type: types_.ENUM, values: Object.keys(FileMode), optional: true, nullable: true} + {name: 'location', type: types_.STRING}, {name: 'onsuccess', type: types_.FUNCTION}, + {name: 'onerror', type: types_.FUNCTION, optional: true, nullable: true}, { + name: 'mode', + type: types_.ENUM, + values: Object.keys(FileMode), + optional: true, + nullable: true + } ]); if (!args.has.mode) { args.mode = 'rw'; + } else if ('rwo' == args.mode) { + throw new WebAPIException(WebAPIException.INVALID_VALUES_ERR, 'rwo mode was introduced in version 5.0 and is not supported in earlier version methods'); } // resolving a path on unmounted storage should result in exception var storage = commonFS_.getStorage(args.location.split('/')[0]); if (storage && FileSystemStorageState.MOUNTED !== storage.state) { setTimeout(function() { - native_.callIfPossible(args.onerror, - new WebAPIException(WebAPIException.NOT_FOUND_ERR, - 'Storage is not mounted.')); + native_.callIfPossible( + args.onerror, + new WebAPIException(WebAPIException.NOT_FOUND_ERR, 'Storage is not mounted.')); }, 0); return; } @@ -58,9 +559,10 @@ function resolve() { if (!result) { // path contains dots - it is not allowed - return InvalidValuesError setTimeout(function() { - native_.callIfPossible(args.onerror, - new WebAPIException(WebAPIException.INVALID_VALUES_ERR, - 'Path contains \'.\' or \'..\' - it is not allowed.')); + native_.callIfPossible( + args.onerror, new WebAPIException( + WebAPIException.INVALID_VALUES_ERR, + 'Path contains \'.\' or \'..\' - it is not allowed.')); }, 0); return; } @@ -70,9 +572,9 @@ function resolve() { if (!_realPath) { // invalid real path means that virtual root does not exist setTimeout(function() { - native_.callIfPossible(args.onerror, - new WebAPIException(WebAPIException.NOT_FOUND_ERR, - 'Specified virtual root does not exist.')); + native_.callIfPossible( + args.onerror, + new WebAPIException(WebAPIException.NOT_FOUND_ERR, 'Invalid path.')); }, 0); return; } @@ -81,16 +583,15 @@ function resolve() { if (args.mode !== 'r' && !_isLocationAllowed) { setTimeout(function() { - native_.callIfPossible(args.onerror, - new WebAPIException(WebAPIException.INVALID_VALUES_ERR, - 'Provided arguments are not valid.')); + native_.callIfPossible( + args.onerror, + new WebAPIException( + WebAPIException.INVALID_VALUES_ERR, 'Provided arguments are not valid.')); }, 0); return; } - var data = { - location: _realPath - }; + var data = {location: _realPath}; var callback = function(result) { if (native_.isFailure(result)) { @@ -101,7 +602,9 @@ function resolve() { var aStatObj = native_.getResultObject(result); var _result = commonFS_.getFileInfo(aStatObj, false, args.mode); if (_result.readOnly && args.mode !== 'r') { - native_.callIfPossible(args.onerror, new WebAPIException(WebAPIException.IO_ERR, 'File is read-only.')); + native_.callIfPossible( + args.onerror, + new WebAPIException(WebAPIException.IO_ERR, 'File is read-only.')); } else { native_.callIfPossible(args.onsuccess, new File(_result)); } @@ -109,9 +612,9 @@ function resolve() { var ret = native_.call('File_stat', data, callback); if (native_.isFailure(ret)) { - throw native_.getErrorObject(ret); + throw native_.getErrorObject(ret); } -}; +} FileSystemManager.prototype.resolve = function() { resolve.apply(this, arguments); @@ -120,8 +623,7 @@ FileSystemManager.prototype.resolve = function() { function getStorage() { xwalk.utils.checkPrivilegeAccess(xwalk.utils.privilege.FILESYSTEM_READ); var args = validator_.validateArgs(arguments, [ - {name: 'label', type: types_.STRING}, - {name: 'onsuccess', type: types_.FUNCTION}, + {name: 'label', type: types_.STRING}, {name: 'onsuccess', type: types_.FUNCTION}, {name: 'onerror', type: types_.FUNCTION, optional: true, nullable: true} ]); @@ -129,13 +631,14 @@ function getStorage() { var storage = commonFS_.getStorage(args.label); if (!storage) { - native_.callIfPossible(args.onerror, + native_.callIfPossible( + args.onerror, new WebAPIException(WebAPIException.NOT_FOUND_ERR, 'Storage not found.')); } else { native_.callIfPossible(args.onsuccess, new FileSystemStorage(storage)); } }, 0); -}; +} FileSystemManager.prototype.getStorage = function() { getStorage.apply(this, arguments); @@ -157,7 +660,7 @@ function listStorages() { native_.callIfPossible(args.onsuccess, storages); }, 0); -}; +} FileSystemManager.prototype.listStorages = function() { listStorages.apply(this, arguments); @@ -202,20 +705,17 @@ function addStorageStateChangeListener() { } return id; -}; +} FileSystemManager.prototype.addStorageStateChangeListener = function() { return addStorageStateChangeListener.apply(this, arguments); }; function removeStorageStateChangeListener() { - var args = validator_.validateArgs(arguments, [ - {name: 'watchId', type: types_.LONG} - ]); + var args = validator_.validateArgs(arguments, [{name: 'watchId', type: types_.LONG}]); if (!arguments.length) { - throw new WebAPIException(WebAPIException.TYPE_MISMATCH_ERR, - 'Missing watchId'); + throw new WebAPIException(WebAPIException.TYPE_MISMATCH_ERR, 'Missing watchId'); } var id = args.watchId; @@ -226,12 +726,13 @@ function removeStorageStateChangeListener() { delete callbacks[id]; if (type_.isEmptyObject(callbacks)) { - var result = native_.callSync('FileSystemManager_removeStorageStateChangeListener', {}); + var result = + native_.callSync('FileSystemManager_removeStorageStateChangeListener', {}); if (native_.isFailure(result)) { throw native_.getErrorObject(result); } } -}; +} FileSystemManager.prototype.removeStorageStateChangeListener = function() { removeStorageStateChangeListener.apply(this, arguments); -- 2.7.4 From d9a7314fecb714ca96af07a3c1c5ce449e6c00cd Mon Sep 17 00:00:00 2001 From: Arkadiusz Pietraszek Date: Thu, 19 Apr 2018 13:19:32 +0200 Subject: [PATCH 14/16] [Filesystem] Added fileHandle methods in new file file_handle.js ACR: http://suprem.sec.samsung.net/jira/browse/TWDAPI-121 Change-Id: I5beefd68490dd86d0cfe5e296201f3fa0ec7160b Signed-off-by: Szymon Jastrzebski Signed-off-by: Arkadiusz Pietraszek Signed-off-by: Jakub Skowron Signed-off-by: Pawel Wasowski --- src/filesystem/filesystem_api.js | 1 + src/filesystem/js/file_handle.js | 522 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 523 insertions(+) create mode 100644 src/filesystem/js/file_handle.js diff --git a/src/filesystem/filesystem_api.js b/src/filesystem/filesystem_api.js index cd2442c..38a9b26 100755 --- a/src/filesystem/filesystem_api.js +++ b/src/filesystem/filesystem_api.js @@ -18,4 +18,5 @@ //= require('common.js'); //= require('file_stream.js'); //= require('file.js'); +//= require('file_handle.js'); //= require('file_system_manager.js'); diff --git a/src/filesystem/js/file_handle.js b/src/filesystem/js/file_handle.js new file mode 100644 index 0000000..50e35b0 --- /dev/null +++ b/src/filesystem/js/file_handle.js @@ -0,0 +1,522 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +function FileHandle(_id, _path, _mode) { + Object.defineProperties(this, { + id: {value: _id, writable: false, enumerable: false}, + path: {value: _path, writable: false, enumerable: false}, + mode: {value: _mode, writable: false, enumerable: false}, + state: {value: 'opened', writable: true, enumerable: false} + }); +} + +FileHandle.prototype.seek = function() { + var args = validator_.validateArgs(arguments, [ + {name: 'offset', type: types_.LONG}, { + name: 'whence', + type: types_.ENUM, + values: type_.getValues(BaseSeekPosition), + optional: true + } + ]); + + if (!(this.state === 'opened')) { + throw new WebAPIException(WebAPIException.IO_ERR, 'FileHandle is not opened'); + } + var data = {id: this.id, offset: args.offset}; + if (undefined === args.whence) { + data.whence = 'BEGIN'; + } else { + data.whence = args.whence; + } + var result = native_.callSync('FileHandle_seek', data); + if (native_.isFailure(result)) { + throw native_.getErrorObject(result); + } + return native_.getResultObject(result); +}; + +FileHandle.prototype.seekNonBlocking = function() { + var args = validator_.validateArgs(arguments, [ + {name: 'offset', type: types_.LONG}, + {name: 'successCallback', type: types_.FUNCTION, optional: true, nullable: true}, + {name: 'errorCallback', type: types_.FUNCTION, optional: true, nullable: true}, { + name: 'whence', + type: types_.ENUM, + values: type_.getValues(BaseSeekPosition), + optional: true + } + ]); + + if (!(this.state === 'opened')) { + throw new WebAPIException(WebAPIException.IO_ERR, 'FileHandle is not opened'); + } + var data = {id: this.id, offset: args.offset, blocking: false}; + if (undefined === args.whence) { + data.whence = 'BEGIN'; + } else { + data.whence = args.whence; + } + + var callback = function(result) { + if (native_.isFailure(result)) { + native_.callIfPossible(args.errorCallback, native_.getErrorObject(result)); + } else { + native_.callIfPossible(args.successCallback, native_.getResultObject(result)); + } + }; + + var result = native_.call('FileHandle_seek', data, callback); + if (native_.isFailure(result)) { + throw native_.getErrorObject(result); + } +}; + +FileHandle.prototype.readString = function() { + var args = validator_.validateArgs(arguments, [ + {name: 'count', type: types_.LONG, optional: true, nullable: true}, + {name: 'inputEncoding', type: types_.STRING, optional: true} + ]); + if (!(this.state === 'opened')) { + throw new WebAPIException(WebAPIException.IO_ERR, 'FileHandle is not opened'); + } + if ((this.mode === 'w') || (this.mode === 'a')) { + throw new WebAPIException(WebAPIException.IO_ERR, 'FileHandle state is write-only'); + } + var data = { + id: this.id, + count: args.count, + inputEncoding: args.inputEncoding + }; + var result = native_.callSync('FileHandle_readString', data); + + if (native_.isFailure(result)) { + throw native_.getErrorObject(result); + } + return native_.getResultObject(result); +}; + +FileHandle.prototype.readStringNonBlocking = function() { + var args = validator_.validateArgs(arguments, [ + {name: 'successCallback', type: types_.FUNCTION, optional: true, nullable: true}, + {name: 'errorCallback', type: types_.FUNCTION, optional: true, nullable: true}, + {name: 'size', type: types_.LONG, optional: true, nullable: true}, + {name: 'inputEncoding', type: types_.STRING, optional: true} + ]); + if (!(this.state === 'opened')) { + throw new WebAPIException(WebAPIException.IO_ERR, 'FileHandle is not opened'); + } + if ((this.mode === 'w') || (this.mode === 'a')) { + throw new WebAPIException(WebAPIException.IO_ERR, 'FileHandle state is write-only'); + } + var data = {id: this.id, size: args.size, inputEncoding: args.inputEncoding, blocking: false}; + + var callback = function(result) { + if (native_.isFailure(result)) { + native_.callIfPossible(args.errorCallback, native_.getErrorObject(result)); + } else { + native_.callIfPossible(args.successCallback, native_.getResultObject(result)); + } + }; + + var result = native_.call('FileHandle_readString', data, callback); + + if (native_.isFailure(result)) { + throw native_.getErrorObject(result); + } +}; + +FileHandle.prototype.writeString = function() { + var args = validator_.validateArgs(arguments, [ + {name: 'string', type: types_.STRING}, + {name: 'outputEncoding', type: types_.STRING, optional: true} + ]); + if (!('opened' === this.state)) { + throw new WebAPIException(WebAPIException.IO_ERR, 'FileHandle is not opened'); + } + if ('r' === this.mode) { + throw new WebAPIException(WebAPIException.IO_ERR, 'FileHandle state is read-only'); + } + var data = {id: this.id, string: args.string, outputEncoding: args.outputEncoding}; + var result = native_.callSync('FileHandle_writeString', data); + + if (native_.isFailure(result)) { + throw native_.getErrorObject(result); + } + return native_.getResultObject(result); +}; + +FileHandle.prototype.writeStringNonBlocking = function() { + var args = validator_.validateArgs(arguments, [ + {name: 'string', type: types_.STRING}, + {name: 'successCallback', type: types_.FUNCTION, optional: true, nullable: true}, + {name: 'errorCallback', type: types_.FUNCTION, optional: true, nullable: true}, + {name: 'outputEncoding', type: types_.STRING, optional: true} + ]); + if (!('opened' === this.state)) { + throw new WebAPIException(WebAPIException.IO_ERR, 'FileHandle is not opened'); + } + if ('r' === this.mode) { + throw new WebAPIException(WebAPIException.IO_ERR, 'FileHandle state is read-only'); + } + var data = {id: this.id, string: args.string, outputEncoding: args.outputEncoding, blocking: false}; + + var callback = function(result) { + if (native_.isFailure(result)) { + native_.callIfPossible(args.errorCallback, native_.getErrorObject(result)); + } else { + native_.callIfPossible(args.successCallback, native_.getResultObject(result)); + } + }; + + var result = native_.call('FileHandle_writeString', data, callback); + + if (native_.isFailure(result)) { + throw native_.getErrorObject(result); + } +}; + +FileHandle.prototype.readBlob = function() { + var args = validator_.validateArgs( + arguments, [{name: 'size', type: types_.LONG, optional: true}]); + + if (!(this.state === 'opened')) { + throw new WebAPIException(WebAPIException.IO_ERR, 'FileHandle is not opened'); + } + if ((this.mode === 'w') || (this.mode === 'a')) { + throw new WebAPIException(WebAPIException.IO_ERR, 'FileHandle state is write-only'); + } + var data = {id: this.id, size: args.size}; + + var result = native_.call('FileHandle_readData', data); + var encodedData = native_.getResultObject(result); + var data = StringToArray(encodedData, Uint8Array); + return new Blob([data]); +}; + +FileHandle.prototype.readBlobNonBlocking = function() { + var args = validator_.validateArgs(arguments, [ + {name: 'successCallback', type: types_.FUNCTION, optional: true, nullable: true}, + {name: 'errorCallback', type: types_.FUNCTION, optional: true, nullable: true}, + {name: 'size', type: types_.LONG, optional: true, nullable: true} + ]); + if (!(this.state === 'opened')) { + throw new WebAPIException(WebAPIException.IO_ERR, 'FileHandle is not opened'); + } + + var data = {id: this.id, size: args.size, blocking: false}; + + var callback = function(result) { + if (native_.isFailure(result)) { + native_.callIfPossible(args.errorCallback, native_.getErrorObject(result)); + } else { + var encodedData = native_.getResultObject(result); + var data = StringToArray(encodedData, Uint8Array); + native_.callIfPossible(args.successCallback, new Blob([data])); + } + }; + + var result = native_.call('FileHandle_readData', data, callback); + + if (native_.isFailure(result)) { + throw native_.getErrorObject(result); + } +}; + +function blobToUint8Array(b) { + var uri = URL.createObjectURL(b), xhr = new XMLHttpRequest(), i, ui8; + xhr.open('GET', uri, false); + xhr.send(); + URL.revokeObjectURL(uri); + var stringUtf8 = unescape(encodeURIComponent(xhr.response)); + ui8 = new Uint8Array(stringUtf8.length); + for (i = 0; i < stringUtf8.length; ++i) { + ui8[i] = stringUtf8.charCodeAt(i); + } + return ui8; +} + +FileHandle.prototype.writeBlob = function() { + var args = validator_.validateArgs( + arguments, [{name: 'blob', type: types_.PLATFORM_OBJECT, values: Blob}]); + if (!(this.state === 'opened')) { + throw new WebAPIException(WebAPIException.IO_ERR, 'FileHandle is not opened'); + } + if (this.mode === 'r') { + throw new WebAPIException(WebAPIException.IO_ERR, 'FileHandle state is read-only'); + } + + var encodedData = ArrayToString(new Uint8Array(blobToUint8Array(args.blob))); + var data = {id: this.id, data: encodedData}; + var result = native_.callSync('FileHandle_writeData', data); + if (native_.isFailure(result)) { + throw native_.getErrorObject(result); + } +}; + +FileHandle.prototype.writeBlobNonBlocking = function() { + var args = validator_.validateArgs(arguments, [ + {name: 'blob', type: types_.PLATFORM_OBJECT, values: Blob}, + {name: 'successCallback', type: types_.FUNCTION, optional: true, nullable: true}, + {name: 'errorCallback', type: types_.FUNCTION, optional: true, nullable: true} + ]); + if (!('opened' === this.state)) { + throw new WebAPIException(WebAPIException.IO_ERR, 'FileHandle is not opened'); + } else if (this.mode === 'r') { + throw new WebAPIException(WebAPIException.IO_ERR, 'FileHandle state is read-only'); + } + + var encodedData = ArrayToString(new Uint8Array(blobToUint8Array(args.blob))); + var data = {id: this.id, data: encodedData, blocking: false}; + var callback = function(result) { + if (native_.isFailure(result)) { + native_.callIfPossible(args.errorCallback, native_.getErrorObject(result)); + } else { + native_.callIfPossible(args.successCallback, native_.getResultObject(result)); + } + }; + + var result = native_.call('FileHandle_writeData', data, callback); + + if (native_.isFailure(result)) { + throw native_.getErrorObject(result); + } +}; + +FileHandle.prototype.readData = function() { + var args = validator_.validateArgs( + arguments, [{name: 'size', type: types_.LONG, optional: true, nullable: true}]); + if (!(this.state === 'opened')) { + throw new WebAPIException(WebAPIException.IO_ERR, 'FileHandle is not opened'); + } + if ((this.mode === 'w') || (this.mode === 'a')) { + throw new WebAPIException(WebAPIException.IO_ERR, 'FileHandle state is write-only'); + } + var data = {id: this.id, size: args.size}; + var result = native_.callSync('FileHandle_readData', data); + + if (native_.isFailure(result)) { + throw native_.getErrorObject(result); + } + var encodedData = native_.getResultObject(result); + var data = StringToArray(encodedData, Uint8Array); + return new Uint8Array(data); +}; + +FileHandle.prototype.readDataNonBlocking = function() { + var args = validator_.validateArgs(arguments, [ + {name: 'successCallback', type: types_.FUNCTION, optional: true, nullable: true}, + {name: 'errorCallback', type: types_.FUNCTION, optional: true, nullable: true}, + {name: 'size', type: types_.LONG, optional: true, nullable: true} + ]); + if (!(this.state === 'opened')) { + throw new WebAPIException(WebAPIException.IO_ERR, 'FileHandle is not opened'); + } + if ((this.mode === 'w') || (this.mode === 'a')) { + throw new WebAPIException(WebAPIException.IO_ERR, 'FileHandle state is write-only'); + } + + var data = {id: this.id, size: args.size, blocking: false}; + + var callback = function(result) { + if (native_.isFailure(result)) { + native_.callIfPossible(args.errorCallback, native_.getErrorObject(result)); + } else { + var data_out = + new Uint8Array(StringToArray(native_.getResultObject(result), Uint8Array)); + native_.callIfPossible(args.successCallback, data_out); + } + }; + + var result = native_.call('FileHandle_readData', data, callback); + + if (native_.isFailure(result)) { + throw native_.getErrorObject(result); + } +}; + +FileHandle.prototype.writeData = function() { + var args = validator_.validateArgs( + arguments, [{name: 'data', type: types_.PLATFORM_OBJECT, values: Uint8Array}]); + if (!(this.state === 'opened')) { + throw new WebAPIException(WebAPIException.IO_ERR, 'FileHandle is not opened'); + } else if (this.mode === 'r') { + throw new WebAPIException(WebAPIException.IO_ERR, 'FileHandle state is read-only'); + } + var encodedData = ArrayToString(args.data); + var data = {id: this.id, data: encodedData}; + var result = native_.callSync('FileHandle_writeData', data); + + if (native_.isFailure(result)) { + throw native_.getErrorObject(result); + } +}; + +FileHandle.prototype.writeDataNonBlocking = function() { + var args = validator_.validateArgs(arguments, [ + {name: 'data', type: types_.PLATFORM_OBJECT, values: Uint8Array}, + {name: 'successCallback', type: types_.FUNCTION, optional: true, nullable: true}, + {name: 'errorCallback', type: types_.FUNCTION, optional: true, nullable: true} + ]); + if (!('opened' === this.state)) { + throw new WebAPIException(WebAPIException.IO_ERR, 'FileHandle is not opened'); + } else if (this.mode === 'r') { + throw new WebAPIException(WebAPIException.IO_ERR, 'FileHandle state is read-only'); + } + + var encodedData = ArrayToString(args.data); + + var data = {id: this.id, data: encodedData, blocking: false}; + + var callback = function(result) { + if (native_.isFailure(result)) { + native_.callIfPossible(args.errorCallback, native_.getErrorObject(result)); + } else { + native_.callIfPossible(args.successCallback, native_.getResultObject(result)); + } + }; + + var result = native_.call('FileHandle_writeData', data, callback); + + if (native_.isFailure(result)) { + throw native_.getErrorObject(result); + } +}; + +FileHandle.prototype.flush = function() { + if (!(this.state === 'opened')) { + throw new WebAPIException(WebAPIException.IO_ERR, 'FileHandle is not opened'); + } + if (this.mode === 'r') { + throw new WebAPIException(WebAPIException.IO_ERR, 'FileHandle state is read-only'); + } + var data = {id: this.id}; + var result = native_.callSync('FileHandle_flush', data); + + if (native_.isFailure(result)) { + throw native_.getErrorObject(result); + } +}; + +FileHandle.prototype.flushNonBlocking = function() { + var args = validator_.validateArgs(arguments, [ + {name: 'successCallback', type: types_.FUNCTION, optional: true, nullable: true}, + {name: 'errorCallback', type: types_.FUNCTION, optional: true, nullable: true} + ]); + if (!(this.state === 'opened')) { + throw new WebAPIException(WebAPIException.IO_ERR, 'FileHandle is not opened'); + } + if (this.mode === 'r') { + throw new WebAPIException(WebAPIException.IO_ERR, 'FileHandle state is read-only'); + } + var data = {id: this.id, blocking: false}; + + var callback = function(result) { + if (native_.isFailure(result)) { + native_.callIfPossible(args.errorCallback, native_.getErrorObject(result)); + } else { + native_.callIfPossible(args.successCallback, native_.getResultObject(result)); + } + }; + + var result = native_.call('FileHandle_flush', data, callback); + + if (native_.isFailure(result)) { + throw native_.getErrorObject(result); + } +}; + +FileHandle.prototype.sync = function() { + if (!(this.state === 'opened')) { + throw new WebAPIException(WebAPIException.IO_ERR, 'FileHandle is not opened'); + } + if (this.mode === 'r') { + throw new WebAPIException(WebAPIException.IO_ERR, 'FileHandle state is read-only'); + } + + var data = {id: this.id}; + var result = native_.callSync('FileHandle_sync', data); + + if (native_.isFailure(result)) { + throw native_.getErrorObject(result); + } +}; + +FileHandle.prototype.syncNonBlocking = function() { + var args = validator_.validateArgs(arguments, [ + {name: 'successCallback', type: types_.FUNCTION, optional: true, nullable: true}, + {name: 'errorCallback', type: types_.FUNCTION, optional: true, nullable: true} + ]); + if (!(this.state === 'opened')) { + throw new WebAPIException(WebAPIException.IO_ERR, 'FileHandle is not opened'); + } + if (this.mode === 'r') { + throw new WebAPIException(WebAPIException.IO_ERR, 'FileHandle state is read-only'); + } + var data = {id: this.id, blocking: false}; + + var callback = function(result) { + if (native_.isFailure(result)) { + native_.callIfPossible(args.errorCallback, native_.getErrorObject(result)); + } else { + native_.callIfPossible(args.successCallback, native_.getResultObject(result)); + } + }; + + var result = native_.call('FileHandle_sync', data, callback); + + if (native_.isFailure(result)) { + throw native_.getErrorObject(result); + } +}; + +FileHandle.prototype.close = function() { + if (!(this.state === 'opened')) { + throw new WebAPIException(WebAPIException.IO_ERR, 'FileHandle is not opened'); + } + var data = {id: this.id}; + var result = native_.callSync('FileHandle_close', data); + this.state = 'closed'; + if (native_.isFailure(result)) { + throw native_.getErrorObject(result); + } +}; + +FileHandle.prototype.closeNonBlocking = function() { + var args = validator_.validateArgs(arguments, [ + {name: 'successCallback', type: types_.FUNCTION, optional: true, nullable: true}, + {name: 'errorCallback', type: types_.FUNCTION, optional: true, nullable: true} + ]); + if (!(this.state === 'opened')) { + throw new WebAPIException(WebAPIException.IO_ERR, 'FileHandle is not opened'); + } + + var data = {id: this.id, blocking: false}; + var callback = function(result) { + if (native_.isFailure(result)) { + native_.callIfPossible(args.errorCallback, native_.getErrorObject(result)); + } else { + native_.callIfPossible(args.successCallback, native_.getResultObject(result)); + } + }; + + var result = native_.call('FileHandle_close', data, callback); + this.state = 'closed'; + if (native_.isFailure(result)) { + throw native_.getErrorObject(result); + } +}; -- 2.7.4 From b1e7effa3f192ef8bd511298a54b8269f0dbd11d Mon Sep 17 00:00:00 2001 From: Arkadiusz Pietraszek Date: Fri, 27 Apr 2018 11:23:56 +0200 Subject: [PATCH 15/16] [Filesystem] Added storege privilage checks ACR: http://suprem.sec.samsung.net/jira/browse/TWDAPI-121 Change-Id: Id146dd02bc37ffc6064a40d77f184f302def3f93 Signed-off-by: Arkadiusz Pietraszek --- src/filesystem/filesystem_instance.cc | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/filesystem/filesystem_instance.cc b/src/filesystem/filesystem_instance.cc index 5c0f341..bd11dcd 100644 --- a/src/filesystem/filesystem_instance.cc +++ b/src/filesystem/filesystem_instance.cc @@ -1286,6 +1286,8 @@ void FilesystemInstance::FileSystemManagerOpenFile(const picojson::value& args, } const std::string& path = args.get("path").get(); + + CHECK_STORAGE_ACCESS(path, &out); const std::string open_mode = GetFopenMode(args); FILE* file = nullptr; try { @@ -1311,6 +1313,7 @@ void FilesystemInstance::FileSystemManagerCreateDirectory(const picojson::value& double callback_id = args.get("callbackId").get(); const std::string& path = args.get("path").get(); bool make_parents = args.get("makeParents").get(); + CHECK_STORAGE_ACCESS(path, &out); this->worker.add_job([this, callback_id, path, make_parents] { picojson::value response = picojson::value(picojson::object()); @@ -1337,6 +1340,7 @@ void FilesystemInstance::FileSystemManagerDeleteFile(const picojson::value& args double callback_id = args.get("callbackId").get(); const std::string& path = args.get("path").get(); + CHECK_STORAGE_ACCESS(path, &out); this->worker.add_job([this, callback_id, path] { picojson::value response = picojson::value(picojson::object()); picojson::object& obj = response.get(); @@ -1361,6 +1365,8 @@ void FilesystemInstance::FileSystemManagerDeleteDirectory(const picojson::value& double callback_id = args.get("callbackId").get(); const std::string& path = args.get("path").get(); + + CHECK_STORAGE_ACCESS(path, &out); bool recursive = args.get("recursive").get(); this->worker.add_job([this, callback_id, path, recursive] { @@ -1399,7 +1405,9 @@ void FilesystemInstance::FileSystemManagerCopyFile(const picojson::value& args, double callback_id = args.get("callbackId").get(); const std::string& path = args.get("path").get(); + CHECK_STORAGE_ACCESS(path, &out); const std::string& destination_path = args.get("destinationPath").get(); + CHECK_STORAGE_ACCESS(destination_path, &out); bool overwrite = args.get("overwrite").get(); this->worker.add_job([this, callback_id, path, destination_path, overwrite] { @@ -1444,7 +1452,9 @@ void FilesystemInstance::FileSystemManagerCopyDirectory(const picojson::value& a CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemRead, &out); CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemWrite, &out); const std::string& path = args.get("path").get(); + CHECK_STORAGE_ACCESS(path, &out); const std::string& destination_path = args.get("destinationPath").get(); + CHECK_STORAGE_ACCESS(destination_path, &out); double callback_id = args.get("callbackId").get(); bool overwrite = args.get("overwrite").get(); @@ -1490,7 +1500,9 @@ void FilesystemInstance::FileSystemManagerMoveFile(const picojson::value& args, CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemWrite, &out); const std::string& path = args.get("path").get(); + CHECK_STORAGE_ACCESS(path, &out); const std::string& destination_path = args.get("destinationPath").get(); + CHECK_STORAGE_ACCESS(destination_path, &out); double callback_id = args.get("callbackId").get(); bool overwrite = args.get("overwrite").get(); @@ -1539,7 +1551,9 @@ void FilesystemInstance::FileSystemManagerMoveDirectory(const picojson::value& a CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemWrite, &out); double callback_id = args.get("callbackId").get(); const std::string& path = args.get("path").get(); + CHECK_STORAGE_ACCESS(path, &out); const std::string& destination_path = args.get("destinationPath").get(); + CHECK_STORAGE_ACCESS(destination_path, &out); bool overwrite = args.get("overwrite").get(); this->worker.add_job([this, callback_id, path, destination_path, overwrite] { @@ -1581,6 +1595,8 @@ void FilesystemInstance::FileSystemManagerRename(const picojson::value& args, ScopeLogger(); CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemWrite, &out); const std::string& path = args.get("path").get(); + + CHECK_STORAGE_ACCESS(path, &out); double callback_id = args.get("callbackId").get(); const std::string& new_name = args.get("newName").get(); @@ -1637,6 +1653,7 @@ void FilesystemInstance::FileSystemManagerListDirectory(const picojson::value& a double callback_id = args.get("callbackId").get(); const std::string& path = args.get("path").get(); const picojson::object& filter = args.get("filter").get(); + CHECK_STORAGE_ACCESS(path, &out); this->worker.add_job([this, callback_id, path, filter] { ScopeLogger(); @@ -1725,6 +1742,8 @@ void FilesystemInstance::FileSystemManagerIsFile(const picojson::value& args, ScopeLogger(); CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemRead, &out); const std::string& path = args.get("path").get(); + + CHECK_STORAGE_ACCESS(path, &out); picojson::value is_file{}; try { struct stat buf {}; @@ -1745,6 +1764,8 @@ void FilesystemInstance::FileSystemManagerIsDirectory(const picojson::value& arg ScopeLogger(); CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemRead, &out); const std::string& path = args.get("path").get(); + + CHECK_STORAGE_ACCESS(path, &out); picojson::value is_directory{}; try { struct stat buf {}; @@ -1765,6 +1786,8 @@ void FilesystemInstance::FileSystemManagerPathExists(const picojson::value& args ScopeLogger(); CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemRead, &out); const std::string& path = args.get("path").get(); + + CHECK_STORAGE_ACCESS(path, &out); picojson::value does_file_exist = picojson::value{true}; try { struct stat buf {}; -- 2.7.4 From 153828a3e3f648c7d3efea5a036bffb48f6fa8da Mon Sep 17 00:00:00 2001 From: Rafal Walczyna Date: Wed, 16 May 2018 14:29:03 +0200 Subject: [PATCH 16/16] [humanactivitymonitor] Fixing rRinterval value in heartRate data rRinterval was always 0. This value is now available, but measurement is not present in unified builds. It was tested on R765 and R600 with solis binaries and it worked fine. [verification] AutoTCT - 100% pass Change-Id: I3c181c53e0832f429774f86277bcaeda4708c0b7 Signed-off-by: Rafal Walczyna --- src/humanactivitymonitor/humanactivitymonitor_manager.cc | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/humanactivitymonitor/humanactivitymonitor_manager.cc b/src/humanactivitymonitor/humanactivitymonitor_manager.cc index 172aeba..ea7a8ce 100644 --- a/src/humanactivitymonitor/humanactivitymonitor_manager.cc +++ b/src/humanactivitymonitor/humanactivitymonitor_manager.cc @@ -1374,15 +1374,18 @@ HumanActivityMonitorManager::HumanActivityMonitorManager() return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "To few values of HRM event"); } - LoggerD("|- values[0]: %f", event->values[0]); - LoggerD("|- values[1]: %f", event->values[1]); + LoggerD("|- values[0][HR ]: %f", event->values[0]); + LoggerD("|- values[1][RRI]: %f", event->values[1]); float hr = floor(event->values[0] + 0.5); // heart beat rate 0 ~ 220 integer (bpm) - // there are no public native api for peak to peak interval. + // there are no public native api documentation for peak to peak interval. // but RRI = (60 / HR) * 1000 - // or unofficially values[1] is rri (0 ~ 5000 ms) - float rri = floor(event->values[1] + 0.5); + // @ 18.05.2018 + // in capi, rri is in values[2], but it is not documented because value can be unstable + // and it is not available on all devices. On solis it works fine. + + float rri = floor(event->values[2] + 0.5); // rr-interval 0 ~ 5000 integer (ms) data->insert(std::make_pair("heartRate", picojson::value(static_cast(hr)))); data->insert(std::make_pair("rRInterval", picojson::value(static_cast(rri)))); -- 2.7.4