Fixes for recursive opendir
authorJanusz Majnert <j.majnert@samsung.com>
Wed, 24 Oct 2012 13:56:01 +0000 (15:56 +0200)
committerJihoon Chung <jihoon.chung@samsung.com>
Fri, 2 Nov 2012 07:07:08 +0000 (16:07 +0900)
[Issue#] N/A
[Feature] Replacing recursive opendir with safe fts_ functions
[Cause] Recursive opendir may reach limit of open dirs for process
[Solution] Replace with standard fts_* functions for traversing directories
[Verification] Run Tizen Filesystem plugin tests

Change-Id: I681c468e373bf65015b1d6162c0cab3ae94af097

src/modules/tizen/Filesystem/Manager.cpp
src/modules/tizen/Filesystem/Node.cpp

index e31c46e..c43044d 100755 (executable)
@@ -22,7 +22,7 @@
 #include <ctime>
 #include <cstdio>
 #include <sstream>
-#include <dirent.h>
+#include <fts.h>
 #include <ecore-1/Ecore_File.h>
 #include <eina-1/eina/eina_list.h>
 #include <dpl/log/log.h>
@@ -188,41 +188,58 @@ void Manager::find(const IPathPtr& path,
 {
     Try {
         Assert(path && "path is NULL");
+        FTS *fts;
+        FTSENT *ftsent;
+        std::string pth=path->getFullPath();
+        char * const paths[] = {const_cast<char * const>(pth.c_str()), NULL};
 
-        DIR* dir = opendir(path->getFullPath().c_str());
-        if (!dir) {
+        if ((fts = fts_open(paths, FTS_PHYSICAL|FTS_NOCHDIR, NULL)) == NULL) {
+            //ERROR
+            int error = errno;
+            LogError(__PRETTY_FUNCTION__ << ": fts_open on "
+                    << pth
+                    << " failed with error: "
+                    << strerror(error));
             return;
         }
 
-        errno = 0;
-        struct dirent* entry = NULL;
-        while ((entry = readdir(dir))) {
-            if (event && event->checkCancelled()) {
-                break;
-            }
-            if (!strncmp(entry->d_name, ".",
-                         1) || !strncmp(entry->d_name, "..", 2)) {
-                continue;
-            }
-            IPathPtr childPath = *path + entry->d_name;
-            struct stat info;
-            memset(&info, 0, sizeof(struct stat));
-            if (lstat(childPath->getFullPath().c_str(), &info) == 0) {
-                if (matchFilters(entry->d_name, info, filters)) {
-                    result.push_back(Node::resolve(childPath));
-                }
-                if (S_ISDIR(info.st_mode)) {
-                    find(childPath, filters, result, event);
-                }
+        while ((ftsent = fts_read(fts)) != NULL) {
+            if(event && event->checkCancelled()) break;
+            switch (ftsent->fts_info) {
+                case FTS_DP:
+                    //directory in postorder - do nothing
+                    break;
+                case FTS_D:
+                case FTS_DC:
+                case FTS_F:
+                case FTS_SL:
+                case FTS_SLNONE:
+                case FTS_DEFAULT:
+                    //regular files, symbolic links, directories in preorder
+                    //and other file entries that can be processed further
+                    if (matchFilters(ftsent->fts_name, *ftsent->fts_statp, filters)) {
+                        IPathPtr childPath=IPath::create(ftsent->fts_path);
+                        result.push_back(Node::resolve(childPath));
+                    }
+                    break;
+                case FTS_NS:
+                case FTS_NSOK:
+                case FTS_DOT:
+                case FTS_DNR:
+                case FTS_ERR:
+                default:
+                    LogWarning(__PRETTY_FUNCTION__
+                            << ": traversal failed with error: "
+                            << strerror(ftsent->fts_errno));
+                    ThrowMsg(Commons::PlatformException,
+                            "Error reading directory");
             }
         }
 
-        if (errno != 0) {
-            ThrowMsg(Commons::PlatformException,
-                     "Error while reading directory.");
-        }
-
-        if (closedir(dir) != 0) {
+        if (fts_close(fts) == -1) {
+            int error = errno;
+            LogWarning(__PRETTY_FUNCTION__ << ": fts_close failed with error: "
+                    << strerror(error));
             ThrowMsg(Commons::PlatformException,
                      "Could not close platform node.");
         }
index 0561cd9..582fcba 100755 (executable)
@@ -20,6 +20,7 @@
 #include <sys/types.h>
 #include <cstdio>
 #include <unistd.h>
+#include <fts.h>
 #include <dirent.h>
 #include <errno.h>
 #include <pcrecpp.h>
@@ -420,45 +421,76 @@ void Node::removeAsDirectory(const IPathPtr& path,
 {
     Assert(path);
     if (recursive) {
-        DIR* dir = opendir(path->getFullPath().c_str());
-        if (!dir) {
-            LogError("File: " << path->getFullPath().c_str());
-            ThrowMsg(Commons::PlatformException,
-                     "Node does not exist or access denied.");
+        FTS *fts;
+        FTSENT *ftsent;
+        int error = 0;
+        std::string pth=path->getFullPath();
+        char * const paths[] = {const_cast<char * const>(pth.c_str()), NULL};
+
+        if ((fts = fts_open(paths, FTS_PHYSICAL|FTS_NOCHDIR, NULL)) == NULL) {
+            //ERROR
+            error = errno;
+            LogError(__PRETTY_FUNCTION__ << ": fts_open on "
+                    << pth
+                    << " failed with error: "
+                    << strerror(error));
+            ThrowMsg(Commons::PlatformException, "Failed to traverse Node");
         }
-        errno = 0;
-        struct dirent *entry = NULL;
-        while ((entry = readdir(dir))) {
-            if (!strncmp(entry->d_name, ".",
-                         1) || !strncmp(entry->d_name, "..", 2)) {
-                continue;
-            }
-            IPathPtr subPath = *path + entry->d_name;
-            struct stat info;
-            memset(&info, 0, sizeof(struct stat));
-            if (lstat(subPath->getFullPath().c_str(), &info) == 0) {
-                Try {
-                    if (S_ISDIR(info.st_mode)) {
-                        removeAsDirectory(subPath, true);
-                    } else if (S_ISREG(info.st_mode)) {
-                        removeAsFile(subPath);
+
+        while ((ftsent = fts_read(fts)) != NULL) {
+            switch (ftsent->fts_info) {
+                case FTS_D:
+                    //directory in preorder - do nothing
+                    break;
+                case FTS_DP:
+                    //directory in postorder - remove
+                    errno = 0;
+                    if (rmdir(ftsent->fts_accpath) != 0) {
+                        if (errno == EEXIST) {
+                            ThrowMsg(Commons::PlatformException,
+                                    "Node has child nodes.");
+                        }
+                        ThrowMsg(Commons::PlatformException,
+                                "Error while removing platform node.");
                     }
-                }
-                Catch(Commons::PlatformException) {
-                }
-                // TODO: Not sure if above exception should be swallowed.
+                    break;
+                case FTS_DC:
+                case FTS_F:
+                case FTS_NSOK:
+                case FTS_SL:
+                case FTS_SLNONE:
+                case FTS_DEFAULT:
+                    {
+                        //regular files and other objects that can safely be removed
+                        IPathPtr file_path = IPath::create(ftsent->fts_path);
+                        removeAsFile(file_path);
+                        break;
+                    }
+                case FTS_NS:
+                case FTS_DOT:
+                case FTS_DNR:
+                case FTS_ERR:
+                default:
+                    LogWarning(__PRETTY_FUNCTION__
+                            << ": traversal failed with error: "
+                            << strerror(ftsent->fts_errno));
+                    break;
             }
         }
-        closedir(dir);
-    }
 
-    errno = 0;
-    if (rmdir(path->getFullPath().c_str()) != 0) {
-        if (errno == EEXIST) {
-            ThrowMsg(Commons::PlatformException, "Node has child nodes.");
+        if (fts_close(fts) == -1) {
+            error = errno;
+            LogWarning(__PRETTY_FUNCTION__ << ": fts_close failed with error: "
+                    << strerror(error));
+        }
+    } else {
+        if (rmdir(path->getFullPath().c_str()) != 0) {
+            if (errno == EEXIST) {
+                ThrowMsg(Commons::PlatformException, "Node has child nodes.");
+            }
+            ThrowMsg(Commons::PlatformException,
+                    "Error while removing platform node.");
         }
-        ThrowMsg(Commons::PlatformException,
-                 "Error while removing platform node.");
     }
 }