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.
24 #include <sys/types.h>
32 #include <Commons/Exception.h>
33 #include "PathUtils.h"
36 #include "NodeFilterMatcher.h"
39 using namespace WrtDeviceApis;
40 using namespace WrtDeviceApis::Commons;
44 namespace Filesystem {
46 #define MAX_NODE_LENGTH 256
47 bool Node::checkPermission(const IPathPtr &path, const std::string &mode, NodeType type)
53 DIR* dir = opendir(path->getFullPath().c_str());
56 ThrowMsg(Commons::SecurityException, "Node has been deleted from platform.");
58 if (closedir(dir) != 0)
59 ThrowMsg(Commons::SecurityException, "Could not close platform node.");
61 LoggerD("open/close dir ok");
71 IPathPtr tempFilePath = IPath::create(path->getFullPath());
72 tempFilePath->append(ss.str());
75 createAsFileInternal(tempFilePath);
76 removeAsFile(tempFilePath);
78 catch (const Commons::Exception& ex)
80 LoggerE("Exception: " << ex.GetMessage());
84 if (mode == "rw" || mode == "w" || mode == "a")
86 ThrowMsg(Commons::InvalidArgumentException, "invalid mode");
92 std::ios_base::openmode modeBit = std::fstream::binary;
96 modeBit |= std::fstream::in;
98 else if (mode == "rw" || mode == "w" || mode == "a")
100 modeBit |= std::fstream::app;
104 ThrowMsg(Commons::InvalidArgumentException, "invalid mode");
107 stream.open(path->getFullPath().c_str(), modeBit);
109 if (stream.is_open())
121 INodePtr Node::resolve(const IPathPtr& path)
126 if (lstat(path->getFullPath().c_str(), &info) != 0) {
127 LoggerE("File: " << path->getFullPath().c_str() << " error no" <<errno);
132 ThrowMsg(Commons::SecurityException, "Node access denied");
135 ThrowMsg(Commons::NotFoundException, "Node does not exist");
138 ThrowMsg(Commons::PlatformException, "Platform exception fail");
142 if (!S_ISDIR(info.st_mode) & !S_ISREG(info.st_mode) && !S_ISLNK(info.st_mode)) {
143 ThrowMsg(Commons::PlatformException,
144 "Platform node is of unsupported type.");
147 NodeType type = S_ISDIR(info.st_mode) ? NT_DIRECTORY : NT_FILE;
149 if (S_ISLNK(info.st_mode)) {
150 syminfo = stat(path);
152 type = S_ISDIR(syminfo.st_mode) ? NT_DIRECTORY : NT_FILE;
156 NodePtr result(new Node(path, type));
158 return DPL::StaticPointerCast<INode>(result);
161 IPathPtr Node::getPath() const
163 return IPath::create(m_path->getFullPath());
166 INodePtr Node::getChild(const IPathPtr& path)
168 if (m_type != NT_DIRECTORY) {
169 ThrowMsg(Commons::PlatformException, "Not a directory.");
171 return Node::resolve(*m_path + *path);
174 NodeType Node::getType() const
179 int Node::getPermissions() const
184 void Node::setPermissions(int perms)
189 Node::NameList Node::getChildNames() const
191 if (m_type != NT_DIRECTORY) {
192 ThrowMsg(Commons::PlatformException, "Node is not directory.");
195 if ((m_perms & PERM_READ) == 0) {
196 ThrowMsg(Commons::SecurityException, "No permission.");
199 DIR* dir = opendir(m_path->getFullPath().c_str());
201 ThrowMsg(Commons::PlatformException,
202 "Node has been deleted from platform.");
207 struct dirent *entry = NULL;
208 while ((entry = readdir(dir))) {
209 if (!strcmp(entry->d_name, ".") || !strncmp(entry->d_name, "..", 2)) {
212 result.push_back(entry->d_name);
215 ThrowMsg(Commons::PlatformException, "Error while reading directory.");
218 if (closedir(dir) != 0) {
219 ThrowMsg(Commons::PlatformException, "Could not close platform node.");
225 NodeList Node::getChildNodes(const NodeFilterPtr& filter) const
227 if (m_type != NT_DIRECTORY) {
228 ThrowMsg(Commons::PlatformException, "Node is not directory.");
231 if ((m_perms & PERM_READ) == 0) {
232 ThrowMsg(Commons::SecurityException, "No permission.");
235 DIR* dir = opendir(m_path->getFullPath().c_str());
237 ThrowMsg(Commons::PlatformException,
238 "Node has been deleted from platform.");
243 struct dirent *entry = NULL;
244 while ((entry = readdir(dir))) {
245 if (!strcmp(entry->d_name, ".") || !strncmp(entry->d_name, "..", 2)) {
249 INodePtr node = Node::resolve(*m_path + entry->d_name);
250 node->setPermissions(getPermissions()); // inherit access rights
251 if (NodeFilterMatcher::match(node, filter)) {
252 result.push_back(node);
255 catch (const Commons::Exception& ex)
261 ThrowMsg(Commons::PlatformException, "Error while reading directory.");
264 if (closedir(dir) != 0) {
265 ThrowMsg(Commons::PlatformException, "Could not close platform node.");
271 void Node::getChildNodes(const EventListNodesPtr& event)
274 EventRequestReceiver<EventListNodes>::PostRequest(event);
277 INodePtr Node::createChild(
278 const IPathPtr& path,
282 if (m_type != NT_DIRECTORY) {
283 ThrowMsg(Commons::PlatformException, "Parent node is not a directory.");
286 if ((m_perms & PERM_WRITE) == 0) {
287 ThrowMsg(Commons::SecurityException, "Not enough permissions.");
290 IPathPtr childPath = *m_path + *path;
291 if (exists(childPath)) {
292 ThrowMsg(Commons::PlatformException, "Node already exists.");
298 result.Reset(createAsFile(childPath, options));
301 result.Reset(createAsDirectory(childPath, options));
304 ThrowMsg(Commons::PlatformException, "Unsupported node type.");
307 result->m_perms = m_perms;
309 ThrowMsg(Commons::PlatformException, "Node creation error");
312 return DPL::StaticPointerCast<INode>(result);
315 IStreamPtr Node::open(int mode)
317 if (m_type == NT_DIRECTORY) {
318 ThrowMsg(Commons::PlatformException,
319 "Cannot attach stream to directory.");
322 if (((mode & AM_READ) && ((m_perms & PERM_READ) == 0)) ||
323 (((mode & AM_WRITE) ||
324 (mode & AM_APPEND)) && ((m_perms & PERM_WRITE) == 0))) {
325 ThrowMsg(Commons::SecurityException, "Not enough permissions.");
328 DPL::Mutex::ScopedLock lock(&m_openStreamsMutex);
329 StreamPtr stream(new Stream(SharedFromThis(), mode));
330 m_openStreams.insert(stream);
331 IManager::getInstance().addOpenedNode(DPL::StaticPointerCast<INode>(
333 return DPL::StaticPointerCast<IStream>(stream);
336 void Node::open(const EventOpenPtr& event)
339 EventRequestReceiver<EventOpen>::PostRequest(event);
342 void Node::remove(int options)
346 removeAsFile(m_path);
349 removeAsDirectory(m_path, (options & OPT_RECURSIVE));
354 unsigned long long Node::getSize() const
356 if (m_type == NT_DIRECTORY) {
357 ThrowMsg(Commons::PlatformException,
358 "Getting size for directories is not supported.");
361 struct stat info = stat(m_path);
362 if (!S_ISREG(info.st_mode)) {
363 ThrowMsg(Commons::PlatformException,
364 "Specified node is not a regular file.");
370 std::time_t Node::getCreated() const
372 return stat(m_path).st_ctime;
375 std::time_t Node::getModified() const
377 return stat(m_path).st_mtime;
380 // TODO Optimize it, maybe store a flag indicating that node is a root.
381 INodePtr Node::getParent() const
383 LocationPaths roots = IManager::getInstance().getLocationPaths();
384 for (LocationPaths::iterator it = roots.begin(); it != roots.end(); ++it) {
385 if (*(*it) == *m_path) {
389 return Node::resolve(IPath::create(m_path->getPath()));
392 int Node::getMode() const
395 struct stat info = stat(m_path);
396 if (info.st_mode & S_IRUSR) { result |= PM_USER_READ; }
397 if (info.st_mode & S_IWUSR) { result |= PM_USER_WRITE; }
398 if (info.st_mode & S_IXUSR) { result |= PM_USER_EXEC; }
399 if (info.st_mode & S_IRGRP) { result |= PM_GROUP_READ; }
400 if (info.st_mode & S_IWGRP) { result |= PM_GROUP_WRITE; }
401 if (info.st_mode & S_IXGRP) { result |= PM_GROUP_EXEC; }
402 if (info.st_mode & S_IROTH) { result |= PM_OTHER_READ; }
403 if (info.st_mode & S_IWOTH) { result |= PM_OTHER_WRITE; }
404 if (info.st_mode & S_IXOTH) { result |= PM_OTHER_EXEC; }
408 void Node::read(const EventReadTextPtr& event)
411 EventRequestReceiver<EventReadText>::PostRequest(event);
414 void Node::onStreamClose(const StreamPtr& stream)
417 DPL::Mutex::ScopedLock lock(&m_openStreamsMutex);
418 m_openStreams.erase(stream);
420 if (m_openStreams.empty()) {
421 IManager::getInstance().removeOpenedNode(DPL::StaticPointerCast<INode>(
426 bool Node::exists(const IPathPtr& path)
429 memset(&info, 0, sizeof(struct stat));
430 int status = lstat(path->getFullPath().c_str(), &info);
436 else if (errno == ENAMETOOLONG)
438 ThrowMsg(Commons::PlatformException, "file name is too long");
440 else if (errno != ENOENT)
447 struct stat Node::stat(const IPathPtr& path)
450 memset(&result, 0, sizeof(struct stat));
451 if (::stat(path->getFullPath().c_str(),
454 LoggerE("File: " << path->getFullPath().c_str());
455 ThrowMsg(Commons::PlatformException, "Node does not exist or no access");
460 Node::Node(const IPathPtr& path,
468 Node* Node::createAsFile(const IPathPtr& path,
472 createAsFileInternal(path);
473 return new Node(path, NT_FILE);
476 void Node::createAsFileInternal(const IPathPtr& path)
479 FILE* file = std::fopen(path->getFullPath().c_str(), "wb");
481 ThrowMsg(Commons::PlatformException,
482 "Platform node could not be created.");
487 Node* Node::createAsDirectory(const IPathPtr& path,
490 if (options & OPT_RECURSIVE) {
491 PathUtils::PathList parts = PathUtils::getParts(path);
492 PathUtils::PathListIterator it = parts.begin();
493 for (; it != parts.end(); ++it) {
494 if (!exists(*it)) { createAsDirectoryInternal(*it); }
497 createAsDirectoryInternal(path);
498 return new Node(path, NT_DIRECTORY);
501 void Node::createAsDirectoryInternal(const IPathPtr& path)
503 if (mkdir(path->getFullPath().c_str(), S_IRWXU | S_IRWXG | S_IROTH |
505 ThrowMsg(Commons::PlatformException,
506 "Platform node could not be created.");
510 void Node::removeAsFile(const IPathPtr& path)
512 DPL::Mutex::ScopedLock lock(&m_openStreamsMutex);
513 if (!m_openStreams.empty()) {
514 ThrowMsg(Commons::PlatformException, "Node is locked for I/O.");
516 if (IManager::getInstance().checkIfOpened(path)) {
517 ThrowMsg(Commons::PlatformException, "Node is locked for I/O.");
520 if (unlink(path->getFullPath().c_str()) != 0) {
521 ThrowMsg(Commons::PlatformException,
522 "Error while removing platform node.");
526 void Node::removeAsDirectory(const IPathPtr& path,
530 DIR* dir = opendir(path->getFullPath().c_str());
532 LoggerE("File: " << path->getFullPath().c_str());
533 ThrowMsg(Commons::PlatformException,
534 "Node does not exist or access denied.");
537 struct dirent *entry = NULL;
538 while ((entry = readdir(dir))) {
539 if (!strcmp(entry->d_name, ".") || !strncmp(entry->d_name, "..", 2)) {
542 IPathPtr subPath = *path + entry->d_name;
544 memset(&info, 0, sizeof(struct stat));
545 if (lstat(subPath->getFullPath().c_str(), &info) == 0) {
547 if (S_ISDIR(info.st_mode)) {
548 removeAsDirectory(subPath, true);
549 } else if (S_ISREG(info.st_mode)) {
550 removeAsFile(subPath);
553 catch (const Commons::Exception& ex)
556 // TODO: Not sure if above exception should be swallowed.
563 if (rmdir(path->getFullPath().c_str()) != 0) {
564 if (errno == EEXIST) {
565 ThrowMsg(Commons::PlatformException, "Node has child nodes.");
567 ThrowMsg(Commons::PlatformException,
568 "Error while removing platform node.");
572 void Node::OnRequestReceived(const EventListNodesPtr& event)
575 NodeList list = event->getNode()->getChildNodes(event->getFilter());
576 event->setResult(list);
578 catch (const Commons::Exception& ex)
580 LoggerE("Exception: " << ex.GetMessage());
581 event->setExceptionCode(ex.getCode());
586 void Node::OnRequestReceived(const EventOpenPtr& event)
588 if (!event->checkCancelled()) {
590 IStreamPtr result = open(event->getMode());
591 result->setCharSet(event->getCharSet());
592 event->setResult(result);
595 catch (const Commons::Exception& ex)
597 LoggerE("Exception: " << ex.GetMessage());
598 event->setExceptionCode(ex.getCode());
600 //event can be cancelled before executing this code.
601 //when it comes here we doesn't allow it anymore
602 event->setCancelAllowed(false);
604 event->setCancelAllowed(true);
608 void Node::OnRequestReceived(const EventReadTextPtr& event)
611 event->setResult(readText());
612 LoggerD("LEAVIN GRACEFULLY");
614 catch (const Commons::Exception& ex)
616 LoggerE("Exception: " << ex.GetMessage());
617 event->setExceptionCode(ex.getCode());
619 //this function doesn't change state of the platform,
620 //so we can allow to cancel it and discard results.
621 event->setCancelAllowed(true);
624 std::string Node::readText()
626 if (m_type != NT_FILE) {
627 ThrowMsg(Commons::PlatformException, "Node is not a file.");
630 if ((m_perms & PERM_READ) == 0) {
631 ThrowMsg(Commons::SecurityException, "No permission.");
634 std::stringstream result;
635 DPL::SharedPtr<Stream> stream(new Stream(SharedFromThis(), AM_READ));
636 while (!stream->isEof()) {
637 result << stream->getLine();
645 std::string Node::toUri(int /*widgetId*/) const
647 // TODO I believe moving this feature to WrtWrapper would make more sense.
648 return "file://" + m_path->getFullPath();