2 // Tizen Web Device API
3 // Copyright (c) 2012 Samsung Electronics Co., Ltd.
5 // Licensed under the Apache License, Version 2.0 (the License);
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
9 // http://www.apache.org/licenses/LICENSE-2.0
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
27 #include <sys/types.h>
35 #include <PlatformException.h>
36 #include <CommonsJavaScript/JSUtils.h>
37 #include <GlobalContextManager.h>
38 #include <JSWebAPIErrorFactory.h>
46 #include "NodeFilterMatcher.h"
48 #include "JSFilestream.h"
49 #include "FilesystemPathUtils.h"
50 #include "Converter.h"
55 namespace Filesystem {
61 #define MAX_NODE_LENGTH 256
62 bool Node::checkPermission(const PathPtr &path, const std::string &mode, NodeType type)
68 DIR* dir = opendir(path->getFullPath().c_str());
71 LOGW("throw InvalidValuesException");
72 throw DeviceAPI::Common::InvalidValuesException(
73 "Node has been deleted from platform.");
76 if (closedir(dir) != 0) {
77 LOGW("throw InvalidValuesException");
78 throw DeviceAPI::Common::InvalidValuesException(
79 "Could not close platform node.");
89 PathPtr tempFilePath = Path::create(path->getFullPath());
90 tempFilePath->append(ss.str());
93 createAsFileInternal(tempFilePath);
94 removeAsFile(tempFilePath);
96 catch (const DeviceAPI::Common::BasePlatformException& ex)
98 LOGW("Exception: %s", ex.getMessage().c_str());
102 if (mode == "rw" || mode == "w" || mode == "a") {
105 LOGW("throw InvalidValuesException");
106 throw DeviceAPI::Common::InvalidValuesException("invalid mode");
112 std::ios_base::openmode modeBit = std::fstream::binary;
116 modeBit |= std::fstream::in;
118 else if (mode == "rw" || mode == "w" || mode == "a")
120 modeBit |= std::fstream::app;
124 LOGW("throw InvalidValuesException");
125 throw DeviceAPI::Common::InvalidValuesException("invalid mode");
128 stream.open(path->getFullPath().c_str(), modeBit);
130 if (stream.is_open())
142 NodePtr DLL_EXPORT Node::resolve(const PathPtr& path)
144 LOGD("Entered path:[%s]", path->getFullPath().c_str());
149 if (lstat(path->getFullPath().c_str(), &info) != 0) {
150 LOGE("File:[%s] error no:%d", path->getFullPath().c_str(), errno);
155 LOGW("throw InvalidValuesException for file:[%s]", path->getFullPath().c_str());
156 throw DeviceAPI::Common::InvalidValuesException("Node access denied");
159 LOGW("throw NotFoundException for file:[%s]", path->getFullPath().c_str());
160 throw DeviceAPI::Common::NotFoundException("NotFoundError");
163 LOGW("throw IOException for file:[%s]", path->getFullPath().c_str());
164 throw DeviceAPI::Common::IOException("Platform exception fail");
168 if (!S_ISDIR(info.st_mode) & !S_ISREG(info.st_mode) && !S_ISLNK(info.st_mode)) {
169 LOGW("throw IOException for file:[%s]", path->getFullPath().c_str());
170 throw DeviceAPI::Common::IOException(
171 "Platform node is of unsupported type.");
174 NodeType type = S_ISDIR(info.st_mode) ? NT_DIRECTORY : NT_FILE;
176 if (S_ISLNK(info.st_mode)) {
177 syminfo = stat(path);
179 type = S_ISDIR(syminfo.st_mode) ? NT_DIRECTORY : NT_FILE;
183 auto result = std::shared_ptr<Node>(new Node(path, type));
185 LOGD("Finished execution for file:[%s] type:%s", path->getFullPath().c_str(),
186 type == NT_DIRECTORY ? "directory" : "file");
190 PathPtr Node::getPath() const
192 return Path::create(m_path->getFullPath());
195 NodePtr Node::getChild(const PathPtr& path)
197 if (m_type != NT_DIRECTORY) {
198 LOGW("throw IOException");
199 throw DeviceAPI::Common::IOException("Not a directory.");
201 return Node::resolve(*m_path + *path);
204 NodeType Node::getType() const
209 int Node::getPermissions() const
214 void Node::setPermissions(int perms)
219 Node::NameList Node::getChildNames() const
221 if (m_type != NT_DIRECTORY) {
222 LOGW("throw IOException");
223 throw DeviceAPI::Common::IOException("Node is not directory.");
226 if ((m_perms & PERM_READ) == 0) {
227 LOGW("throw InvalidValuesException");
228 throw DeviceAPI::Common::InvalidValuesException("No permission.");
231 DIR* dir = opendir(m_path->getFullPath().c_str());
233 LOGW("throw IOException");
234 throw DeviceAPI::Common::IOException(
235 "Node has been deleted from platform.");
240 struct dirent *entry = NULL;
241 while ((entry = readdir(dir))) {
242 if (!strcmp(entry->d_name, ".") || !strncmp(entry->d_name, "..", 2)) {
245 result.push_back(entry->d_name);
248 LOGW("throw IOException");
249 throw DeviceAPI::Common::IOException("Error while reading directory.");
252 if (closedir(dir) != 0) {
253 LOGW("throw IOException");
254 throw DeviceAPI::Common::IOException("Could not close platform node.");
260 NodeList Node::getChildNodes(const NodeFilterPtr& filter) const
262 if (m_type != NT_DIRECTORY) {
263 LOGW("Path %s Perm %d", m_path->getFullPath().c_str(), m_perms);
264 LOGW("throw IOException");
265 throw DeviceAPI::Common::IOException("Node is not directory.");
266 LOGW("Path %s Perm %d", m_path->getFullPath().c_str(), m_perms);
269 if ((m_perms & PERM_READ) == 0) {
270 LOGW("Path %s Perm %d", m_path->getFullPath().c_str(), m_perms);
271 LOGW("throw InvalidValuesException");
272 throw DeviceAPI::Common::InvalidValuesException("No permission.");
275 DIR* dir = opendir(m_path->getFullPath().c_str());
277 LOGW("Path %s Perm %d", m_path->getFullPath().c_str(), m_perms);
278 LOGW("throw IOException");
279 throw DeviceAPI::Common::IOException(
280 "Node has been deleted from platform.");
285 struct dirent *entry = NULL;
286 while ((entry = readdir(dir))) {
287 if (!strcmp(entry->d_name, ".") || !strncmp(entry->d_name, "..", 2)) {
291 NodePtr node = Node::resolve(*m_path + entry->d_name);
292 node->setPermissions(getPermissions()); // inherit access rights
293 if (NodeFilterMatcher::match(node, filter)) {
294 result.push_back(node);
297 catch (const DeviceAPI::Common::BasePlatformException& ex)
299 LOGW("caught BasePlatformException ignored");
304 LOGW("Path %s Perm %d", m_path->getFullPath().c_str(), m_perms);
305 LOGW("throw IOException");
306 throw DeviceAPI::Common::IOException("Error while reading directory.");
309 if (closedir(dir) != 0) {
310 LOGW("Path %s Perm %d", m_path->getFullPath().c_str(), m_perms);
311 LOGW("throw IOException");
312 throw DeviceAPI::Common::IOException("Could not close platform node.");
319 void Node::getChildNodesWorker(EventListNodesPtr aEvent)
321 NodeList list = aEvent->getNode()->getChildNodes(aEvent->getFilter());
322 aEvent->setResult(list);
325 void Node::getChildNodesEnd(EventListNodesPtr aEvent)
327 // Convert results into JSCore
328 auto result = aEvent->getResult();
329 Converter converter(aEvent->getContext());
331 JSValueRef jsResult = converter.toJSValueRef(
333 aEvent->getParentPermissions(),
334 aEvent->getContext());
335 aEvent->callSuccessCallback(jsResult);
338 void Node::getChildNodes(const EventListNodesPtr& event)
340 LOGD("getChildNodes begin");
341 // This function executes three functions:
342 // 1. Main initialization.
344 // 3. Main results relay.
345 Utils::executeThreadAndMainFunctions<EventListNodesPtr>(
348 Node::getChildNodesWorker,
349 Node::getChildNodesEnd);
350 LOGD("getChildNodes end");
353 NodePtr Node::createChild(
358 if (m_type != NT_DIRECTORY) {
359 LOGW("throw IOException");
360 throw DeviceAPI::Common::IOException("Parent node is not a directory.");
363 if ((m_perms & PERM_WRITE) == 0) {
364 LOGW("throw InvalidValuesException");
365 throw DeviceAPI::Common::InvalidValuesException(
366 "Not enough permissions.");
369 PathPtr childPath = *m_path + *path;
370 if (exists(childPath)) {
371 LOGW("throw IOException");
372 throw DeviceAPI::Common::IOException("Node already exists.");
378 result = createAsFile(childPath, options);
381 result = createAsDirectory(childPath, options);
384 LOGW("throw IOException");
385 throw DeviceAPI::Common::IOException("Unsupported node type.");
388 result->m_perms = m_perms;
390 LOGW("throw IOException");
391 throw DeviceAPI::Common::IOException("Node creation error");
397 StreamPtr Node::open(int mode)
399 if (m_type == NT_DIRECTORY) {
400 LOGW("throw IOException");
401 throw DeviceAPI::Common::IOException(
402 "Cannot attach stream to directory.");
405 if (((mode & AM_READ) && ((m_perms & PERM_READ) == 0)) ||
406 (((mode & AM_WRITE) ||
407 (mode & AM_APPEND)) && ((m_perms & PERM_WRITE) == 0))) {
408 LOGW("throw InvalidValuesException");
409 throw DeviceAPI::Common::InvalidValuesException(
410 "Not enough permissions.");
413 auto stream = std::shared_ptr<Stream>(new Stream(shared_from_this(), mode));
417 void Node::openBegin(EventOpenPtr aEvent)
419 StreamPtr result = aEvent->getNode()->open(aEvent->getMode());
420 result->setCharSet(aEvent->getCharSet());
421 aEvent->setResult(result);
424 void Node::openEnd(EventOpenPtr aEvent)
426 // Convert results into JSCore
427 auto result = aEvent->getResult();
428 Converter converter(aEvent->getContext());
430 JSObjectRef object = JSFilestream::makeJSObject(aEvent->getContext(), result,
431 &Manager::getInstance());
433 JSValueRef jsResult = converter.toJSValueRef(object);
434 aEvent->callSuccessCallback(jsResult);
437 void Node::open(const EventOpenPtr& event)
440 // This function executes three functions:
441 // 1. Main initialization.
443 // 3. Main results relay.
444 Utils::executeThreadAndMainFunctions<EventOpenPtr>(
452 void DLL_EXPORT Node::remove(int options)
456 removeAsFile(m_path);
459 removeAsDirectory(m_path, (options & OPT_RECURSIVE));
462 LOGE("throw UnknownError");
463 throw DeviceAPI::Common::UnknownException(
464 "Not supported value of m_type");
468 unsigned long long Node::getSize() const
470 if (m_type == NT_DIRECTORY) {
471 LOGW("throw IOException");
472 throw DeviceAPI::Common::IOException(
473 "Getting size for directories is not supported.");
476 struct stat info = stat(m_path);
477 if (!S_ISREG(info.st_mode)) {
478 LOGW("throw IOException");
479 throw DeviceAPI::Common::IOException(
480 "Specified node is not a regular file.");
486 std::time_t Node::getCreated() const
488 return stat(m_path).st_ctime;
491 std::time_t Node::getModified() const
493 return stat(m_path).st_mtime;
496 // TODO Optimize it, maybe store a flag indicating that node is a root.
497 NodePtr Node::getParent() const
499 LocationPaths roots = Manager::getInstance().getLocationPaths();
500 for (LocationPaths::iterator it = roots.begin(); it != roots.end(); ++it) {
501 if (*(*it) == *m_path) {
505 NodePtr parent = Node::resolve(Path::create(m_path->getPath()));
506 parent->setPermissions(getPermissions());
510 int Node::getMode() const
513 struct stat info = stat(m_path);
514 if (info.st_mode & S_IRUSR) { result |= PM_USER_READ; }
515 if (info.st_mode & S_IWUSR) { result |= PM_USER_WRITE; }
516 if (info.st_mode & S_IXUSR) { result |= PM_USER_EXEC; }
517 if (info.st_mode & S_IRGRP) { result |= PM_GROUP_READ; }
518 if (info.st_mode & S_IWGRP) { result |= PM_GROUP_WRITE; }
519 if (info.st_mode & S_IXGRP) { result |= PM_GROUP_EXEC; }
520 if (info.st_mode & S_IROTH) { result |= PM_OTHER_READ; }
521 if (info.st_mode & S_IWOTH) { result |= PM_OTHER_WRITE; }
522 if (info.st_mode & S_IXOTH) { result |= PM_OTHER_EXEC; }
526 void Node::readBegin(EventReadTextPtr aEvent)
528 auto thiz = aEvent->getNode();
529 // Check access privileges
530 if (thiz->m_type != NT_FILE) {
531 LOGW("throw IOException");
532 throw DeviceAPI::Common::IOException("Node is not a file.");
534 if ((thiz->m_perms & PERM_READ) == 0) {
535 LOGW("throw IOException");
536 throw DeviceAPI::Common::IOException("No permission.");
540 std::shared_ptr<Stream>(new Stream(thiz->shared_from_this(), AM_READ));
541 aEvent->setStream(stream);
544 void Node::readWorker(EventReadTextPtr aEvent)
546 // Read file in text mode
548 auto inputStream = aEvent->getStream();
549 while (!inputStream->isEof()) {
550 buffer += inputStream->getLine();
551 if (!inputStream->isEof()) {
555 //Convert string into UTF8
556 auto currentEncoding = aEvent->getCharSet();
557 std::shared_ptr<std::string> utf8OutStr(new std::string());
558 if (!buffer.empty()) {
559 Utils::toUTF8String(currentEncoding,
564 aEvent->setResult(utf8OutStr);
567 void Node::readEnd(EventReadTextPtr aEvent)
569 // Convert results into JSCore
570 auto stream = aEvent->getStream();
572 auto nativeResult = aEvent->getResult();
573 Converter converter(aEvent->getContext());
575 JSValueRef jsResult = converter.toJSValueRef(*nativeResult);
576 aEvent->callSuccessCallback(jsResult);
579 gboolean Node::readAsTextCallbackExecutor(void* aEvent)
582 EventHandler* handl = static_cast<EventHandler*>(aEvent);
584 LOGE("handl is NULL");
588 auto event = handl->ptr;
590 JSContextRef context = event->getContext();
591 if (!DeviceAPI::Common::GlobalContextManager::getInstance()->
592 isAliveGlobalContext(context)) {
593 LOGE("context was closed");
598 if (event->isError()) {
599 LOGE("Error callback invoke");
600 event->callErrorCallback(
601 DeviceAPI::Common::JSWebAPIErrorFactory::makeErrorObject(context,
602 event->getExceptionName(),event->getExceptionMessage()));
604 event->callSuccessCallback(
605 DeviceAPI::Common::JSUtil::toJSValueRef(context, *event->getResult()));
620 void* Node::readAsTextThread(void* handler)
622 EventHandler* handl = static_cast<EventHandler*>(handler);
623 auto event = handl->ptr;
625 auto node = event->getNode();
626 // Check access privileges
627 if (NT_FILE != node->m_type) {
628 LOGW("throw IOException");
629 throw DeviceAPI::Common::IOException("Node is not a file.");
631 if (0 == (node->m_perms & PERM_READ)) {
632 LOGW("throw IOException");
633 throw DeviceAPI::Common::IOException("No permission.");
637 std::shared_ptr<Stream>(new Stream(node->shared_from_this(), AM_READ));
639 while (!inputStream->isEof()) {
640 buffer += inputStream->getLine();
641 if (!inputStream->isEof()) {
645 inputStream->close();
646 //Convert string into UTF8
647 auto currentEncoding = event->getCharSet();
648 std::shared_ptr<std::string> utf8OutStr(new std::string());
649 if (!buffer.empty()) {
650 Utils::toUTF8String(currentEncoding,
655 event->setResult(utf8OutStr);
657 guint id = g_idle_add(readAsTextCallbackExecutor, handler);
659 LOGE("g_idle_add fails");
662 }catch(const DeviceAPI::Common::BasePlatformException& err){
663 LOGE("Error %s , message %s", err.getName().c_str(), err.getMessage().c_str());
664 event->setException(err.getName(), err.getMessage());
665 event->setIsError(true);
669 LOGE("Unknown Exception");
670 event->setException("", "Unknown error");
671 event->setIsError(true);
677 void Node::readAsText(const EventReadTextPtr& aReadData)
679 EventHandler* handl = new EventHandler();
680 handl->ptr = aReadData;
682 if(pthread_create(&thread, NULL, readAsTextThread,
683 static_cast<void*>(handl))) {
684 LOGE("Thread creation failed");
686 throw DeviceAPI::Common::UnknownException("Thread creation failed");
688 if(pthread_detach(thread)) {
689 LOGE("Thread detachment failed");
693 bool Node::exists(const PathPtr& path)
696 memset(&info, 0, sizeof(struct stat));
697 int status = lstat(path->getFullPath().c_str(), &info);
703 else if (ENAMETOOLONG == errno)
705 LOGW("throw IOException");
706 throw DeviceAPI::Common::IOException("file name is too long");
708 else if (errno != ENOENT)
715 struct stat Node::stat(const PathPtr& path)
718 memset(&result, 0, sizeof(struct stat));
719 if (::stat(path->getFullPath().c_str(),
722 LOGE("File: %s", path->getFullPath().c_str());
723 LOGW("throw IOException");
724 throw DeviceAPI::Common::IOException("Node does not exist or no access");
729 Node::Node(const PathPtr& path, NodeType type):
736 NodePtr Node::createAsFile(const PathPtr& path,
739 createAsFileInternal(path);
740 return std::shared_ptr<Node>(new Node(path, NT_FILE));
743 void Node::createAsFileInternal(const PathPtr& path)
745 FILE* file = std::fopen(path->getFullPath().c_str(), "wb");
747 LOGW("fopen fails IOException throw for path [%s]",
748 path->getFullPath().c_str());
749 throw DeviceAPI::Common::IOException(
750 "Platform node could not be created.");
755 NodePtr Node::createAsDirectory(const PathPtr& path,
758 if (options & OPT_RECURSIVE) {
759 auto parts = Utils::getParts(path);
760 for (auto it = parts.begin(); it != parts.end(); ++it) {
761 if (!exists(*it)) { createAsDirectoryInternal(*it); }
764 createAsDirectoryInternal(path);
765 return std::shared_ptr<Node>(new Node(path, NT_DIRECTORY));
768 void Node::createAsDirectoryInternal(const PathPtr& path)
770 if (mkdir(path->getFullPath().c_str(), S_IRWXU | S_IRWXG | S_IROTH |
772 LOGW("throw IOException");
773 throw DeviceAPI::Common::IOException(
774 "Platform node could not be created.");
778 void Node::removeAsFile(const PathPtr& path)
780 auto fullPath = path->getFullPath();
781 if (unlink(fullPath.c_str()) != 0) {
782 LOGW("remove [%s]", fullPath.c_str());
783 LOGW("throw IOException");
784 throw DeviceAPI::Common::IOException(
785 "Error while removing platform node.");
789 void Node::removeAsDirectory(const PathPtr& path,
793 DIR* dir = opendir(path->getFullPath().c_str());
795 LOGW("File %s", path->getFullPath().c_str());
796 LOGW("throw IOException");
797 throw DeviceAPI::Common::IOException(
798 "Node does not exist or access denied.");
801 struct dirent *entry = NULL;
802 while ((entry = readdir(dir))) {
803 if (!strcmp(entry->d_name, ".") || !strncmp(entry->d_name, "..", 2)) {
806 PathPtr subPath = *path + entry->d_name;
808 memset(&info, 0, sizeof(struct stat));
809 if (lstat(subPath->getFullPath().c_str(), &info) == 0) {
811 if (S_ISDIR(info.st_mode)) {
812 removeAsDirectory(subPath, true);
813 } else if (S_ISREG(info.st_mode)) {
814 removeAsFile(subPath);
817 catch (const DeviceAPI::Common::BasePlatformException& ex)
820 // TODO: Not sure if above exception should be swallowed.
827 if (rmdir(path->getFullPath().c_str()) != 0) {
828 if (errno == EEXIST) {
829 LOGW("throw IOException");
830 throw DeviceAPI::Common::IOException("Node has child nodes.");
832 LOGW("throw IOException");
833 throw DeviceAPI::Common::IOException(
834 "Error while removing platform node.");
838 std::string Node::toUri(int /*widgetId*/) const
840 // TODO I believe moving this feature to WrtWrapper would make more sense.
841 return "file://" + m_path->getFullPath();
844 bool Node::isSubPath(std::string aDirPath, PathPtr aFilePath)
846 auto myPath = aDirPath;
847 if(!myPath.empty() && myPath[myPath.length()-1] != Path::getSeparator()) {
848 myPath += Path::getSeparator();
849 LOGD("updated aDirPath to:%s", aDirPath.c_str());
852 auto childPath = aFilePath->getFullPath();
853 bool isSubP = strncmp(myPath.c_str(), childPath.c_str(), myPath.size()) == 0;
855 LOGD("aDirPath:%s myPath:%s aFilePath:%s isSubP:%d",
858 aFilePath->getFullPath().c_str(),
864 bool Node::isSubPath(PathPtr aPath) const
866 LOGD("Entered thisPath:%s aPath: %s",
867 this->getPath()->getFullPath().c_str(),
868 aPath->getFullPath().c_str());
872 bool isSubP = isSubPath(m_path->getFullPath(), aPath);
873 LOGD("thisPath:%s aPath: %s isSubP:%d",
874 this->getPath()->getFullPath().c_str(),
875 aPath->getFullPath().c_str(),