2 * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
20 #include <sys/types.h>
28 #include <Commons/Exception.h>
29 #include <Filesystem/PathUtils.h>
30 #include <Filesystem/Enums.h>
32 #include "NodeFilterMatcher.h"
34 namespace WrtDeviceApis {
35 namespace Filesystem {
38 INodePtr Node::resolve(const IPathPtr& path)
41 if (lstat(path->getFullPath().c_str(), &info) != 0) {
42 LogError("File: " << path->getFullPath().c_str());
43 ThrowMsg(Commons::PlatformException,
44 "Node does not exist or access denied.");
47 if (!S_ISDIR(info.st_mode) && !S_ISREG(info.st_mode)) {
48 ThrowMsg(Commons::PlatformException,
49 "Platform node is of unsupported type.");
52 NodeType type = S_ISDIR(info.st_mode) ? NT_DIRECTORY : NT_FILE;
53 NodePtr result(new Node(path, type));
54 return DPL::StaticPointerCast<INode>(result);
57 IPathPtr Node::getPath() const
59 return IPath::create(m_path->getFullPath());
62 INodePtr Node::getChild(const IPathPtr& path)
64 if (m_type != NT_DIRECTORY) {
65 ThrowMsg(Commons::PlatformException, "Not a directory.");
67 return Node::resolve(*m_path + *path);
70 NodeType Node::getType() const
75 int Node::getPermissions() const
80 void Node::setPermissions(int perms)
85 Node::NameList Node::getChildNames() const
87 if (m_type != NT_DIRECTORY) {
88 ThrowMsg(Commons::PlatformException, "Node is not directory.");
91 if ((m_perms & PERM_READ) == 0) {
92 ThrowMsg(Commons::SecurityException, "No permission.");
95 DIR* dir = opendir(m_path->getFullPath().c_str());
97 ThrowMsg(Commons::PlatformException,
98 "Node has been deleted from platform.");
105 struct dirent *entry_result;
106 for (return_code = readdir_r(dir, &entry, &entry_result);
107 entry_result != NULL && return_code == 0;
108 return_code = readdir_r(dir, &entry, &entry_result)) {
109 if (!strncmp(entry.d_name, ".", 1) ||
110 !strncmp(entry.d_name, "..", 2))
114 result.push_back(entry.d_name);
116 if (return_code != 0 || errno != 0) {
117 ThrowMsg(Commons::PlatformException, "Error while reading directory.");
120 if (closedir(dir) != 0) {
121 ThrowMsg(Commons::PlatformException, "Could not close platform node.");
127 NodeList Node::getChildNodes(const NodeFilterPtr& filter) const
129 if (m_type != NT_DIRECTORY) {
130 ThrowMsg(Commons::PlatformException, "Node is not directory.");
133 if ((m_perms & PERM_READ) == 0) {
134 ThrowMsg(Commons::SecurityException, "No permission.");
137 DIR* dir = opendir(m_path->getFullPath().c_str());
139 ThrowMsg(Commons::PlatformException,
140 "Node has been deleted from platform.");
147 struct dirent *entry_result;
148 for (return_code = readdir_r(dir, &entry, &entry_result);
149 entry_result != NULL && return_code == 0;
150 return_code = readdir_r(dir, &entry, &entry_result)) {
151 if (!strncmp(entry.d_name, ".", 1) ||
152 !strncmp(entry.d_name, "..", 2))
158 INodePtr node = Node::resolve(*m_path + entry.d_name);
159 node->setPermissions(getPermissions()); // inherit access rights
160 if (NodeFilterMatcher::match(node, filter)) {
161 result.push_back(node);
164 Catch(Commons::PlatformException) {}
167 if (return_code != 0 || errno != 0) {
168 ThrowMsg(Commons::PlatformException, "Error while reading directory.");
171 if (closedir(dir) != 0) {
172 ThrowMsg(Commons::PlatformException, "Could not close platform node.");
178 void Node::getChildNodes(const EventListNodesPtr& event)
181 EventRequestReceiver<EventListNodes>::PostRequest(event);
184 INodePtr Node::createChild(
185 const IPathPtr& path,
189 if (m_type != NT_DIRECTORY) {
190 ThrowMsg(Commons::PlatformException, "Parent node is not a directory.");
193 if ((m_perms & PERM_WRITE) == 0) {
194 ThrowMsg(Commons::SecurityException, "Not enough permissions.");
199 IPathPtr childPath = *m_path + *path;
200 if (exists(childPath)) {
201 ThrowMsg(Commons::PlatformException, "Node already exists.");
207 result.Reset(createAsFile(childPath, options));
210 result.Reset(createAsDirectory(childPath, options));
213 ThrowMsg(Commons::PlatformException, "Unsupported node type.");
216 result->m_perms = m_perms;
218 ThrowMsg(Commons::PlatformException, "Node creation error");
221 return DPL::StaticPointerCast<INode>(result);
224 IStreamPtr Node::open(int mode)
226 if (m_type == NT_DIRECTORY) {
227 ThrowMsg(Commons::PlatformException,
228 "Cannot attach stream to directory.");
231 if (((mode & AM_READ) && ((m_perms & PERM_READ) == 0)) ||
232 (((mode & AM_WRITE) ||
233 (mode & AM_APPEND)) && ((m_perms & PERM_WRITE) == 0)))
235 ThrowMsg(Commons::SecurityException, "Not enough permissions.");
238 DPL::Mutex::ScopedLock lock(&m_openStreamsMutex);
239 StreamPtr stream(new Stream(SharedFromThis(), mode));
240 m_openStreams.insert(stream);
241 IManager::getInstance().addOpenedNode(DPL::StaticPointerCast<INode>(
243 return DPL::StaticPointerCast<IStream>(stream);
246 void Node::open(const EventOpenPtr& event)
249 EventRequestReceiver<EventOpen>::PostRequest(event);
252 void Node::remove(int options)
256 removeAsFile(m_path);
259 removeAsDirectory(m_path, (options & OPT_RECURSIVE));
264 std::size_t Node::getSize() const
266 if (m_type == NT_DIRECTORY) {
267 ThrowMsg(Commons::PlatformException,
268 "Getting size for directories is not supported.");
271 struct stat info = stat(m_path);
272 if (!S_ISREG(info.st_mode)) {
273 ThrowMsg(Commons::PlatformException,
274 "Specified node is not a regular file.");
280 std::time_t Node::getCreated() const
282 return stat(m_path).st_ctime;
285 std::time_t Node::getModified() const
287 return stat(m_path).st_mtime;
290 // TODO Optimize it, maybe store a flag indicating that node is a root.
291 INodePtr Node::getParent() const
293 LocationPaths roots = IManager::getInstance().getLocationPaths();
294 for (LocationPaths::iterator it = roots.begin(); it != roots.end(); ++it) {
297 if (*(*it) == *m_path) {
301 return Node::resolve(IPath::create(m_path->getPath()));
304 int Node::getMode() const
307 struct stat info = stat(m_path);
308 if (info.st_mode & S_IRUSR) {
309 result |= PM_USER_READ;
311 if (info.st_mode & S_IWUSR) {
312 result |= PM_USER_WRITE;
314 if (info.st_mode & S_IXUSR) {
315 result |= PM_USER_EXEC;
317 if (info.st_mode & S_IRGRP) {
318 result |= PM_GROUP_READ;
320 if (info.st_mode & S_IWGRP) {
321 result |= PM_GROUP_WRITE;
323 if (info.st_mode & S_IXGRP) {
324 result |= PM_GROUP_EXEC;
326 if (info.st_mode & S_IROTH) {
327 result |= PM_OTHER_READ;
329 if (info.st_mode & S_IWOTH) {
330 result |= PM_OTHER_WRITE;
332 if (info.st_mode & S_IXOTH) {
333 result |= PM_OTHER_EXEC;
338 void Node::read(const EventReadTextPtr& event)
341 EventRequestReceiver<EventReadText>::PostRequest(event);
344 void Node::onStreamClose(const StreamPtr& stream)
347 DPL::Mutex::ScopedLock lock(&m_openStreamsMutex);
348 m_openStreams.erase(stream);
350 if (m_openStreams.empty()) {
351 IManager::getInstance().removeOpenedNode(DPL::StaticPointerCast<INode>(
356 bool Node::exists(const IPathPtr& path)
359 memset(&info, 0, sizeof(struct stat));
360 int status = lstat(path->getFullPath().c_str(), &info);
361 if ((status == 0) || ((status != 0) && (errno != ENOENT))) {
367 struct stat Node::stat(const IPathPtr& path)
370 memset(&result, 0, sizeof(struct stat));
371 if (::stat(path->getFullPath().c_str(),
374 LogError("File: " << path->getFullPath().c_str());
375 ThrowMsg(Commons::PlatformException, "Node does not exist or no access");
380 Node::Node(const IPathPtr& path,
387 Node* Node::createAsFile(const IPathPtr& path,
391 createAsFileInternal(path);
392 return new Node(path, NT_FILE);
395 void Node::createAsFileInternal(const IPathPtr& path)
398 FILE* file = std::fopen(path->getFullPath().c_str(), "wb");
400 ThrowMsg(Commons::PlatformException,
401 "Platform node could not be created.");
406 Node* Node::createAsDirectory(const IPathPtr& path,
409 if (options & OPT_RECURSIVE) {
410 PathUtils::PathList parts = PathUtils::getParts(path);
411 PathUtils::PathListIterator it = parts.begin();
412 for (; it != parts.end(); ++it) {
414 createAsDirectoryInternal(*it);
418 createAsDirectoryInternal(path);
419 return new Node(path, NT_DIRECTORY);
422 void Node::createAsDirectoryInternal(const IPathPtr& path)
424 if (mkdir(path->getFullPath().c_str(), S_IRWXU | S_IRWXG | S_IROTH |
427 ThrowMsg(Commons::PlatformException,
428 "Platform node could not be created.");
432 void Node::removeAsFile(const IPathPtr& path)
434 DPL::Mutex::ScopedLock lock(&m_openStreamsMutex);
435 if (!m_openStreams.empty()) {
436 ThrowMsg(Commons::PlatformException, "Node is locked for I/O.");
438 if (IManager::getInstance().checkIfOpened(path)) {
439 ThrowMsg(Commons::PlatformException, "Node is locked for I/O.");
442 if (unlink(path->getFullPath().c_str()) != 0) {
443 ThrowMsg(Commons::PlatformException,
444 "Error while removing platform node.");
448 void Node::removeAsDirectory(const IPathPtr& path,
456 std::string pth = path->getFullPath();
457 char * const paths[] = { const_cast<char * const>(pth.c_str()), NULL };
460 fts_open(paths, FTS_PHYSICAL | FTS_NOCHDIR, NULL)) == NULL)
464 LogError(__PRETTY_FUNCTION__ << ": fts_open on "
466 << " failed with error: "
468 ThrowMsg(Commons::PlatformException, "Failed to traverse Node");
471 while ((ftsent = fts_read(fts)) != NULL) {
472 switch (ftsent->fts_info) {
474 //directory in preorder - do nothing
477 //directory in postorder - remove
479 if (rmdir(ftsent->fts_accpath) != 0) {
480 if (errno == EEXIST) {
481 ThrowMsg(Commons::PlatformException,
482 "Node has child nodes.");
484 ThrowMsg(Commons::PlatformException,
485 "Error while removing platform node.");
495 //regular files and other objects that can safely be removed
496 IPathPtr file_path = IPath::create(ftsent->fts_path);
497 removeAsFile(file_path);
505 LogWarning(__PRETTY_FUNCTION__
506 << ": traversal failed with error: "
507 << strerror(ftsent->fts_errno));
512 if (fts_close(fts) == -1) {
514 LogWarning(__PRETTY_FUNCTION__ << ": fts_close failed with error: "
518 if (rmdir(path->getFullPath().c_str()) != 0) {
519 if (errno == EEXIST) {
520 ThrowMsg(Commons::PlatformException, "Node has child nodes.");
522 ThrowMsg(Commons::PlatformException,
523 "Error while removing platform node.");
528 void Node::OnRequestReceived(const EventListNodesPtr& event)
531 NodeList list = event->getNode()->getChildNodes(event->getFilter());
532 event->setResult(list);
533 } catch (const Commons::PlatformException& ex) {
534 LogError("Exception: " << ex.GetMessage());
535 event->setExceptionCode(Commons::ExceptionCodes::PlatformException);
536 } catch (const Commons::SecurityException& ex) {
537 LogError("Exception: " << ex.GetMessage());
538 event->setExceptionCode(Commons::ExceptionCodes::SecurityException);
542 void Node::OnRequestReceived(const EventOpenPtr& event)
544 if (!event->checkCancelled()) {
546 IStreamPtr result = open(event->getMode());
547 event->setResult(result);
548 } catch (const Commons::PlatformException& ex) {
549 LogError("Exception: " << ex.GetMessage());
550 event->setExceptionCode(Commons::ExceptionCodes::PlatformException);
551 } catch (const Commons::SecurityException& ex) {
552 LogError("Exception: " << ex.GetMessage());
553 event->setExceptionCode(Commons::ExceptionCodes::SecurityException);
555 //event can be cancelled before executing this code.
556 //when it comes here we doesn't allow it anymore
557 event->setCancelAllowed(false);
559 event->setCancelAllowed(true);
563 void Node::OnRequestReceived(const EventReadTextPtr& event)
566 event->setResult(readText());
567 LogDebug("LEAVIN GRACEFULLY");
569 Catch(Commons::PlatformException) {
570 event->setExceptionCode(Commons::ExceptionCodes::PlatformException);
572 Catch(Commons::SecurityException) {
573 event->setExceptionCode(Commons::ExceptionCodes::SecurityException);
575 //this function doesn't change state of the platform,
576 //so we can allow to cancel it and discard results.
577 event->setCancelAllowed(true);
580 std::string Node::readText()
582 if (m_type != NT_FILE) {
583 ThrowMsg(Commons::PlatformException, "Node is not a file.");
586 if ((m_perms & PERM_READ) == 0) {
587 ThrowMsg(Commons::SecurityException, "No permission.");
590 std::stringstream result;
591 DPL::SharedPtr<Stream> stream(new Stream(SharedFromThis(), AM_READ));
592 while (!stream->isEof()) {
593 result << stream->getLine();
594 if (!stream->isEof()) {
602 std::string Node::toUri(int /*widgetId*/) const
604 // TODO I believe moving this feature to WrtWrapper would make more sense.
605 return "file://" + m_path->getFullPath();