Initialize Tizen 2.3
[framework/web/wrt-plugins-common.git] / src_wearable / modules / tizen / Filesystem / Manager.cpp
1 /*
2  * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
3  *
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
7  *
8  *        http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 #include "Manager.h"
17
18 #include <unistd.h>
19 #include <sys/stat.h>
20 #include <errno.h>
21 #include <pcrecpp.h>
22 #include <ctime>
23 #include <cstdio>
24 #include <sstream>
25 #include <fts.h>
26 #include <ecore-1/Ecore_File.h>
27 #include <eina-1/eina/eina_list.h>
28 #include <dpl/log/log.h>
29 #include <dpl/scoped_ptr.h>
30 #include <dpl/errno_string.h>
31 #include <Commons/Exception.h>
32 #include <Commons/Regex.h>
33 #include <Filesystem/PathUtils.h>
34 #include "Node.h"
35 #include "Utils.h"
36
37 namespace {
38 const char* PATH_ROOT = "/opt/media";
39 const char* PATH_DOWNLOADS = "/opt/media/Downloads";
40 const char* PATH_DOCUMENTS = "/opt/media/Documents";
41 const char* PATH_SOUNDS = "/opt/media/Music";
42 const char* PATH_IMAGES = "/opt/media/Images";
43 const char* PATH_VIDEOS = "/opt/media/Videos";
44 const char* PATH_SDCARD = "/opt/storage/sdcard";
45 }
46
47 namespace WrtDeviceApis {
48 namespace Filesystem {
49 using namespace Api;
50
51 Manager::Locations Manager::m_locations;
52 const std::size_t Manager::m_maxPathLength = 256;
53 NodeList Manager::m_openedNodes;
54
55 bool Manager::fileExists(const std::string &file)
56 {
57     errno = 0;
58     struct stat info;
59     memset(&info, 0, sizeof(struct stat));
60     int status = lstat(file.c_str(), &info);
61     if (status == 0) {
62         return true;
63     } else if (errno == ENOENT) {
64         return false;
65     }
66     ThrowMsg(Commons::PlatformException, "Cannot stat file.");
67 }
68
69 void Manager::addOpenedNode(const INodePtr& node)
70 {
71     NodeListIterator it = m_openedNodes.begin();
72     for (; it != m_openedNodes.end(); ++it) {
73         if (node.Get() == (*it).Get()) {
74             //node is added already
75             return;
76         }
77     }
78     m_openedNodes.push_back(node);
79 }
80
81 void Manager::removeOpenedNode(const INodePtr& node)
82 {
83     NodeListIterator it = m_openedNodes.begin();
84     for (; it != m_openedNodes.end(); ++it) {
85         if ((*it).Get() == node.Get()) {
86             m_openedNodes.erase(it);
87             break;
88         }
89     }
90 }
91
92 bool Manager::checkIfOpened(const IPathPtr& path) const
93 {
94     Assert(path);
95     NodeListIterator it = m_openedNodes.begin();
96     for (; it != m_openedNodes.end(); ++it) {
97         Assert(*it);
98         if (*path == *(*it)->getPath()) {
99             return true;
100         }
101     }
102     return false;
103 }
104
105 Manager::Manager()
106 {
107     static bool initialized = init();
108     (void) initialized;
109 }
110
111 Manager::~Manager()
112 {}
113
114 IPathPtr Manager::getBasePath() const
115 {
116     Locations::const_iterator it = m_locations.find(LT_ROOT);
117     if (it == m_locations.end()) {
118         ThrowMsg(Commons::PlatformException, "Base path not available.");
119     }
120     return it->second;
121 }
122
123 IPathPtr Manager::getLocationPath(LocationType type) const
124 {
125     Locations::const_iterator it = m_locations.find(type);
126     if (it != m_locations.end()) {
127         return it->second->clone();
128     }
129     return IPathPtr();
130 }
131
132 LocationPaths Manager::getLocationPaths() const
133 {
134     LocationPaths result;
135     Locations::const_iterator it = m_locations.begin();
136     for (; it != m_locations.end(); ++it) {
137         result.push_back(it->second->clone());
138     }
139     return result;
140 }
141
142 LocationTypes Manager::getLocations() const
143 {
144     LocationTypes result;
145     Locations::const_iterator it = m_locations.begin();
146     for (; it != m_locations.end(); ++it) {
147         result.push_back(it->first);
148     }
149     return result;
150 }
151
152 void Manager::getNode(const EventResolvePtr& event)
153 {
154     EventRequestReceiver<EventResolve>::PostRequest(event);
155 }
156
157 std::size_t Manager::getMaxPathLength() const
158 {
159     return m_maxPathLength;
160 }
161
162 void Manager::copy(const EventCopyPtr& event)
163 {
164     EventRequestReceiver<EventCopy>::PostRequest(event);
165 }
166
167 void Manager::move(const EventMovePtr& event)
168 {
169     EventRequestReceiver<EventMove>::PostRequest(event);
170 }
171
172 void Manager::remove(const EventRemovePtr& event)
173 {
174     EventRequestReceiver<EventRemove>::PostRequest(event);
175 }
176
177 void Manager::find(const EventFindPtr& event)
178 {
179     EventRequestReceiver<EventFind>::PostRequest(event);
180 }
181
182 void Manager::find(const IPathPtr& path,
183                    const FiltersMap& filters,
184                    NodeList& result,
185                    const EventFindPtr& event)
186 {
187     Try {
188         AssertMsg(path, "path is NULL");
189         FTS *fts;
190         FTSENT *ftsent;
191         std::string pth = path->getFullPath();
192         char * const paths[] = { const_cast<char * const>(pth.c_str()), NULL };
193
194         if ((fts =
195                  fts_open(paths, FTS_PHYSICAL | FTS_NOCHDIR, NULL)) == NULL)
196         {
197             //ERROR
198             int error = errno;
199             LogError(__PRETTY_FUNCTION__ << ": fts_open on "
200                                          << pth
201                                          << " failed with error: "
202                                          << strerror(error));
203             return;
204         }
205
206         while ((ftsent = fts_read(fts)) != NULL) {
207             if (event && event->checkCancelled()) {
208                 break;
209             }
210             switch (ftsent->fts_info) {
211             case FTS_DP:
212                 //directory in postorder - do nothing
213                 break;
214             case FTS_D:
215             case FTS_DC:
216             case FTS_F:
217             case FTS_SL:
218             case FTS_SLNONE:
219             case FTS_DEFAULT:
220                 //regular files, symbolic links, directories in preorder
221                 //and other file entries that can be processed further
222                 if (matchFilters(ftsent->fts_name, *ftsent->fts_statp,
223                                  filters))
224                 {
225                     IPathPtr childPath = IPath::create(ftsent->fts_path);
226                     result.push_back(Node::resolve(childPath));
227                 }
228                 break;
229             case FTS_NS:
230             case FTS_NSOK:
231             case FTS_DOT:
232             case FTS_DNR:
233             case FTS_ERR:
234             default:
235                 LogWarning(__PRETTY_FUNCTION__
236                            << ": traversal failed with error: "
237                            << strerror(ftsent->fts_errno));
238                 ThrowMsg(Commons::PlatformException,
239                          "Error reading directory");
240             }
241         }
242
243         if (fts_close(fts) == -1) {
244             int error = errno;
245             LogWarning(__PRETTY_FUNCTION__ << ": fts_close failed with error: "
246                                            << strerror(error));
247             ThrowMsg(Commons::PlatformException,
248                      "Could not close platform node.");
249         }
250     }
251     Catch(Commons::Exception) {}
252 }
253
254 void Manager::copyElement(
255     const std::string &src, const std::string &dest, bool recursive) const
256 {
257     LogDebug("Copying src: " << src << " to: " << dest);
258
259     //element is a file:
260     if (EINA_TRUE != ecore_file_is_dir(src.c_str())) {
261         if (EINA_TRUE != ecore_file_cp(src.c_str(), dest.c_str())) {
262             ThrowMsg(Commons::PlatformException, "Failed to copy file");
263         }
264         return;
265     }
266     //element is a directory -> create it:
267     if (EINA_TRUE != ecore_file_mkdir(dest.c_str())) {
268         LogDebug("Failed to create destination directory");
269         ThrowMsg(Commons::PlatformException, "Failed to copy directory");
270     }
271     //copy all elements of directory:
272     if (recursive) {
273         Eina_List* list = ecore_file_ls(src.c_str());
274         void* data;
275         EINA_LIST_FREE(list, data)
276         {
277             Try
278             {
279                 copyElement((src + '/' + static_cast<char*>(data)).c_str(),
280                             (dest + '/' + static_cast<char*>(data)).c_str());
281             }
282             Catch(Commons::PlatformException)
283             {
284                 //remove rest of the list
285                 EINA_LIST_FREE(list, data)
286                 {
287                     free(data);
288                 }
289                 ReThrowMsg(Commons::PlatformException, "Failed to copy element");
290             }
291             free(data);
292         }
293     }
294 }
295
296 bool Manager::access(const IPathPtr& path,
297                      int accessType) const
298 {
299     int amode = 0;
300     if (accessType & AT_EXISTS) {
301         amode |= F_OK;
302     }
303     if (accessType & AT_READ) {
304         amode |= R_OK;
305     }
306     if (accessType & AT_WRITE) {
307         amode |= W_OK;
308     }
309     if (accessType & AT_EXEC) {
310         amode |= X_OK;
311     }
312     return (::access(path->getFullPath().c_str(), amode) == 0);
313 }
314
315 bool Manager::matchFilters(const std::string& name,
316                            const struct stat& info,
317                            const FiltersMap& filters)
318 {
319     FiltersMap::const_iterator it = filters.begin();
320     for (; it != filters.end(); ++it) {
321         if (it->first == FF_NAME) {
322             if (!pcrecpp::RE(it->second).PartialMatch(name)) {
323                 return false;
324             }
325         } else if (it->first == FF_SIZE) {
326             std::size_t size;
327             std::stringstream ss(it->second);
328             ss >> size;
329             if (!ss ||
330                 (size != static_cast<size_t>(info.st_size)))
331             {
332                 return false;
333             }
334         } else if (it->first == FF_CREATED) {
335             std::time_t created;
336             std::stringstream ss(it->second);
337             ss >> created;
338             if (!ss || (created != info.st_ctime)) {
339                 return false;
340             }
341         } else if (it->first == FF_MODIFIED) {
342             std::time_t modified;
343             std::stringstream ss(it->second);
344             ss >> modified;
345             if (!ss || (modified != info.st_mtime)) {
346                 return false;
347             }
348         }
349     }
350     return true;
351 }
352
353 void Manager::OnRequestReceived(const EventResolvePtr& event)
354 {
355     try {
356         event->setResult(Node::resolve(event->getPath()));
357     } catch (const Commons::PlatformException& ex) {
358         LogError("Exception: " << ex.GetMessage());
359         event->setExceptionCode(Commons::ExceptionCodes::PlatformException);
360     }
361     event->setCancelAllowed(true);
362 }
363
364 void Manager::checkPaths(
365     Api::IPathPtr &src,
366     Api::IPathPtr &dest)
367 {
368     Assert(dest);
369     Assert(src);
370     if (!dest->isAbsolute()) {
371         dest = src->getPath() + *dest;
372     }
373
374     if (src == dest) {
375         ThrowMsg(Commons::PlatformException,
376                  "Destination is same as source: " << src->getFullPath());
377     }
378
379     INodePtr parent;
380     Try {
381         parent = Node::resolve(IPath::create(dest->getPath()));
382     }
383     Catch(Commons::PlatformException) {
384         ReThrowMsg(Commons::PlatformException,
385                    "Could not get destination's parent node.");
386     }
387
388     if (parent->getType() != NT_DIRECTORY) {
389         ThrowMsg(Commons::PlatformException,
390                  "Destination's parent node is not directory.");
391     }
392
393     if (!access(parent->getPath(), AT_WRITE)) {
394         ThrowMsg(Commons::SecurityException,
395                  "Not enough permissions to write to destination.");
396     }
397 }
398
399 bool Manager::pathExists(const std::string &path)
400 {
401     errno = 0;
402     struct stat info;
403     memset(&info, 0, sizeof(struct stat));
404     int status = lstat(path.c_str(), &info);
405     if ((status != 0) && (errno != ENOENT)) {
406         ThrowMsg(Commons::PlatformException,
407                  "No access to platform destination node.");
408     }
409     return 0 == status;
410 }
411
412 void Manager::OnRequestReceived(const EventCopyPtr& event)
413 {
414     Try {
415         INodePtr srcNode = Node::resolve(event->getSource());
416         int requiredAccess;
417         switch (srcNode->getType()) {
418         case NT_DIRECTORY:
419             requiredAccess = AT_EXEC;
420             break;
421         case NT_FILE:
422             requiredAccess = AT_READ;
423             break;
424         }
425         if (!access(srcNode->getPath(), requiredAccess)) {
426             ThrowMsg(Commons::SecurityException,
427                      "Not enough permissions to copy source node.");
428         }
429
430         IPathPtr src = event->getSource();
431         IPathPtr dest = event->getDestination();
432
433         checkPaths(src, dest);
434
435         std::string realSrc = src->getFullPath();
436         std::string realDest = dest->getFullPath();
437
438         if (pathExists(realDest)) {
439             //no owerwrite flag setted -> exception
440             if ((event->getOptions() & OPT_OVERWRITE) == 0) {
441                 ThrowMsg(Commons::PlatformException, "Overwrite is not set.");
442             }
443
444             if (event->checkCancelled()) {
445                 //file is not copied yet, so we can cancel it now.
446                 event->setCancelAllowed(true);
447                 return;
448             }
449
450             //destination exist. Need to be removed
451             Try {
452                 INodePtr node = Node::resolve(event->getDestination());
453                 node->remove(event->getOptions());
454             }
455             Catch(Commons::PlatformException) {
456                 LogError("Exception: " << _rethrown_exception.GetMessage());
457                 event->setExceptionCode(
458                     Commons::ExceptionCodes::PlatformException);
459             }
460             Catch(Commons::SecurityException) {
461                 event->setExceptionCode(
462                     Commons::ExceptionCodes::SecurityException);
463             }
464         }
465         //Destination is not exist. Start copy now.
466         copyElement(realSrc, realDest);
467
468         event->setResult(Node::resolve(dest));
469     } catch (const Commons::PlatformException& ex) {
470         LogError("Exception: " << ex.GetMessage());
471         event->setExceptionCode(Commons::ExceptionCodes::PlatformException);
472     } catch (const Commons::SecurityException& ex) {
473         LogError("Exception: " << ex.GetMessage());
474         event->setExceptionCode(Commons::ExceptionCodes::SecurityException);
475     }
476     //file is copied already so we don't allow cancelling anymore.
477     event->setCancelAllowed(false);
478 }
479
480 void Manager::OnRequestReceived(const EventMovePtr& event)
481 {
482     try {
483         IPathPtr src = event->getSource();
484         IPathPtr dest = event->getDestination();
485
486         INodePtr srcNode = Node::resolve(src);
487         if (!access(srcNode->getParent()->getPath(), AT_WRITE)) {
488             ThrowMsg(Commons::SecurityException,
489                      "Not enough permissions to move source node.");
490         }
491
492         checkPaths(src, dest);
493
494         bool destExists = pathExists(dest->getFullPath());
495
496         if (destExists && (0 == (event->getOptions() & OPT_OVERWRITE))) {
497             ThrowMsg(Commons::PlatformException, "Overwrite is not set.");
498         }
499
500         if (event->checkCancelled()) {
501             //file is not moved yet, so we can cancel it now.
502             event->setCancelAllowed(true);
503             return;
504         }
505
506         errno = 0;
507         if (0 != ::rename(src->getFullPath().c_str(),
508                           dest->getFullPath().c_str()))
509         {
510             int error = errno;
511             switch (error) {
512             case EXDEV:
513             {
514                 if (destExists) {
515                     //destination exist. Need to be removed
516                     Try {
517                         INodePtr node = Node::resolve(
518                                 event->getDestination());
519                         node->remove(event->getOptions());
520                     }
521                     Catch(Commons::PlatformException) {
522                         LogError("Exception while removing dest directory");
523                         event->setExceptionCode(
524                             Commons::ExceptionCodes::PlatformException);
525                     }
526                     Catch(Commons::SecurityException) {
527                         event->setExceptionCode(
528                             Commons::ExceptionCodes::SecurityException);
529                     }
530                 }
531
532                 copyElement(src->getFullPath(),
533                             dest->getFullPath());
534                 //remove source files
535                 Try {
536                     INodePtr node = Node::resolve(event->getSource());
537                     node->remove(event->getOptions());
538                 }
539                 Catch(Commons::Exception) {
540                     LogError("Exception: "
541                              << _rethrown_exception.GetMessage());
542                 }
543                 break;
544             }
545             default:
546                 ThrowMsg(Commons::PlatformException,
547                          "Error on rename: " << DPL::GetErrnoString(error));
548                 break;
549             }
550         }
551
552         event->setResult(Node::resolve(dest));
553     } catch (const Commons::PlatformException& ex) {
554         LogError("Exception: " << ex.GetMessage());
555         event->setExceptionCode(Commons::ExceptionCodes::PlatformException);
556     } catch (const Commons::SecurityException& ex) {
557         LogError("Exception: " << ex.GetMessage());
558         event->setExceptionCode(Commons::ExceptionCodes::SecurityException);
559     }
560     event->setCancelAllowed(false);
561 }
562
563 void Manager::OnRequestReceived(const EventRemovePtr& event)
564 {
565     if (!event->checkCancelled()) {
566         Try {
567             INodePtr node = Node::resolve(event->getPath());
568             node->remove(event->getOptions());
569         }
570         Catch(Commons::PlatformException) {
571             LogError("Exception: " << _rethrown_exception.GetMessage());
572             event->setExceptionCode(Commons::ExceptionCodes::PlatformException);
573         }
574         Catch(Commons::SecurityException) {
575             event->setExceptionCode(Commons::ExceptionCodes::SecurityException);
576         }
577         event->setCancelAllowed(false);
578     } else {
579         event->setCancelAllowed(true);
580     }
581 }
582
583 void Manager::OnRequestReceived(const EventFindPtr& event)
584 {
585     try {
586         NodeList result;
587         find(event->getPath(), event->getFilters(), result, event);
588         event->setResult(result);
589     } catch (const Commons::Exception& ex) {
590         LogError("Exception: " << ex.GetMessage());
591         event->setExceptionCode(Commons::ExceptionCodes::PlatformException);
592     }
593     event->setCancelAllowed(true);
594 }
595
596 bool Manager::init()
597 {
598     m_locations[LT_ROOT] = IPath::create(PATH_ROOT);
599     m_locations[LT_SDCARD] = IPath::create(PATH_SDCARD);
600     setupLocation(LT_DOWNLOADS, PATH_DOWNLOADS);
601     setupLocation(LT_DOCUMENTS, PATH_DOCUMENTS);
602     setupLocation(LT_SOUNDS, PATH_SOUNDS);
603     setupLocation(LT_IMAGES, PATH_IMAGES);
604     setupLocation(LT_VIDEOS, PATH_VIDEOS);
605
606     return true;
607 }
608
609 void Manager::setupLocation(LocationType location,
610                             const char* path)
611 {
612     if (!nodeExists(path)) {
613         try {
614             makePath(path, 0755);
615         } catch (const Commons::PlatformException& ex) {
616             LogError("Exception: " << ex.DumpToString());
617             return;
618         }
619     }
620     m_locations[location] = IPath::create(path);
621 }
622 } // Filesystem
623 } // WrtDeviceApis