Merge "Revert "Fix Jira issue (db lock issue)"" into tizen_2.1
[platform/framework/native/appfw.git] / src / io / FIo_DirectoryImpl.cpp
1 //
2 // Open Service Platform
3 // Copyright (c) 2012 Samsung Electronics Co., Ltd.
4 //
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
8 //
9 //     http://www.apache.org/licenses/LICENSE-2.0
10 //
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.
16 //
17
18 /**
19  * @file        FIo_DirectoryImpl.cpp
20  * @brief       This is the implementation file for %_DirectoryImpl class.
21  */
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <unistd.h>
26 #include <string.h>
27 #include <dirent.h>
28 #include <sys/stat.h>
29 #include <limits.h>
30 #include <errno.h>
31 #include <unique_ptr.h>
32
33 #include <FBaseResult.h>
34 #include <FAppPkgPackageInfo.h>
35 #include <FIoFile.h>
36 #include <FIoDirectory.h>
37 #include <FSysDeviceManager.h>
38 #include <FBaseSysLog.h>
39
40 #include <FBase_StringConverter.h>
41 #include <FBase_NativeError.h>
42 #include <FApp_AppInfo.h>
43
44 #include "FIo_FileImpl.h"
45 #include "FIo_DirectoryImpl.h"
46 #include "FIo_DirEnumeratorImpl.h"
47
48 using namespace std;
49 using namespace Tizen::Base;
50 using namespace Tizen::App;
51 using namespace Tizen::System;
52
53 namespace Tizen { namespace Io
54 {
55
56 _DirectoryImpl::_DirectoryImpl(void)
57         : __pDir(null)
58 {
59 }
60
61 _DirectoryImpl::~_DirectoryImpl(void)
62 {
63         result r = E_SUCCESS;
64
65         if (__pDir)
66         {
67                 if (closedir(static_cast <DIR*>(__pDir)) != 0)
68                 {
69                         r = _NativeError::ConvertNativeErrorToResult(errno, true);
70                         SysLog(NID_IO, "[%s] Failed to close the file.", GetErrorMessage(r));
71                 }
72
73                 __pDir = null;
74         }
75 }
76
77 result
78 _DirectoryImpl::Construct(const String& dirPath)
79 {
80         result r = E_SUCCESS;
81
82         SysAssertf(__pDir == null, "Already constructed. Calling Construct() twice or more on a same instance is not allowed for this class\n");
83
84         SysTryReturnResult(NID_IO, _FileImpl::VerifyFilePathCompatibility(dirPath, _AppInfo::IsOspCompat()) == true,
85                         E_INVALID_ARG, " [%ls] is not compatible.", dirPath.GetPointer());
86
87         unique_ptr<char[]> pDirPath(_StringConverter::CopyToCharArrayN(dirPath));
88         SysTryReturn(NID_IO, pDirPath != null, GetLastResult(), GetLastResult(),
89                         "[%s] Invalid dir path.", GetErrorMessage(GetLastResult()));
90
91         __pDir = opendir(pDirPath.get());
92         if (__pDir == null)
93         {
94                 r = _NativeError::ConvertNativeErrorToResult(errno, true);
95                 SysSecureLogException(NID_IO, r, "[%s] Failed to open directory (%s).", GetErrorMessage(r), pDirPath.get());
96                 goto CATCH;
97         }
98
99         __dirPath = dirPath;
100
101         return E_SUCCESS;
102
103 CATCH:
104         return r;
105 }
106
107 DirEnumerator*
108 _DirectoryImpl::ReadN(void)
109 {
110         SysAssertf(__pDir != null, "Not yet constructed. Construct() should be called before use.\n");
111
112         unique_ptr<DirEnumerator> pDirEnum(_DirEnumeratorImpl::CreateDirEnumeratorInstanceN(__dirPath));
113         SysTryReturn(NID_IO, pDirEnum != null, null, E_OUT_OF_MEMORY,
114                            "[E_OUT_OF_MEMORY] The memory is insufficient.");
115
116         return pDirEnum.release();
117 }
118
119 result
120 _DirectoryImpl::Create(const String& dirPath, bool createParentDirsToo)
121 {
122         result r = E_SUCCESS;
123         int ret = 0;
124
125         SysTryReturnResult(NID_IO, _FileImpl::VerifyFilePathCompatibility(dirPath, _AppInfo::IsOspCompat()) == true,
126                         E_INVALID_ARG, " [%ls] is not compatible.", dirPath.GetPointer());
127
128         unique_ptr<char[]> pTempDirPath(_StringConverter::CopyToCharArrayN(dirPath));
129         SysTryReturn(NID_IO, pTempDirPath != null, GetLastResult(), GetLastResult(),
130                         "[%s] Invalid dir path.", GetErrorMessage(GetLastResult()));
131
132         unique_ptr<char[]> pDirPath(new (std::nothrow) char[strlen(pTempDirPath.get()) + 2]);
133         SysTryReturnResult(NID_IO, pDirPath != null, E_OUT_OF_MEMORY, "The memory is insufficient.");
134
135         strcpy(pDirPath.get(), pTempDirPath.get());
136         pDirPath[strlen(pTempDirPath.get())] = '/';
137         pDirPath[strlen(pTempDirPath.get()) + 1] = '\0';
138
139         // By the time we are here.. we assume we have a legal path to create
140         // the below logic works under the assumption that the path name does not end with '/'
141         ret = mkdir(pDirPath.get(), 0755);
142         if (ret == -1)
143         {
144                 // if parent directory does not exist and if we are asked to create them
145                 if ((errno == ENOENT) && (createParentDirsToo == true))
146                 {
147                         // move till we find the directory which does not exist
148                         char* pPos = pDirPath.get();
149
150                         // if the dir path starts with '/', skip it
151                         if (*pPos == '/')
152                         {
153                                 pPos++;
154                         }
155
156                         // scan for the next path seperator ie., '/'.
157                         while (1)
158                         {
159                                 if (pPos && *pPos == '/')
160                                 {
161                                         *pPos = '\0';
162                                         if (access(pDirPath.get(), F_OK) != 0)
163                                         {
164                                                 break;
165                                         }
166                                         *pPos = '/';
167                                 }
168                                 pPos++;
169                         }
170
171                         // found a dir which is not present and realDirPath has that name
172                         while (1)
173                         {
174                                 ret = mkdir(pDirPath.get(), S_IRUSR | S_IWUSR | S_IXUSR);
175                                 if (ret == -1 && errno != EEXIST)
176                                 {
177                                         SysLog(NID_IO, "mkdir() failed. errno: %d (%s), path: %ls",
178                                                         errno, strerror(errno), dirPath.GetPointer());
179                                         r = _NativeError::ConvertNativeErrorToResult(errno, true);
180                                         goto CATCH;
181                                 }
182
183                                 // find for the next dir path
184                                 *pPos = '/';
185
186                                 while (pPos && *pPos == '/')
187                                 {
188                                         pPos++; // skip all occurances of consecutive '/' from current position
189                                 }
190
191                                 if (pPos == null)
192                                 {
193                                         goto CATCH; // E_SUCCESS
194                                 }
195
196                                 pPos = strchr(pPos, '/');
197                                 if (pPos != null)
198                                 {
199                                         *pPos = '\0';
200                                 }
201                                 else
202                                 {
203                                         goto CATCH; // E_SUCCESS
204                                 }
205                         }
206                 }
207                 else
208                 {
209                         if (errno == ENAMETOOLONG)
210                         {
211                                 r = E_INVALID_ARG;
212                         }
213                         else
214                         {
215                                 r = _NativeError::ConvertNativeErrorToResult(errno, true);
216                         }
217                         SysLog(NID_IO, "[%s] Failed to create directory (%s) errno: %d (%s).",
218                                         GetErrorMessage(r), dirPath.GetPointer(), errno, strerror(errno));
219                         goto CATCH;
220                 }
221         }
222
223         // fall through
224 CATCH:
225
226         return r;
227 }
228
229 result
230 _DirectoryImpl::Remove(const String& dirPath, bool recursive)
231 {
232         SysTryReturnResult(NID_IO, _FileImpl::VerifyFilePathCompatibility(dirPath, _AppInfo::IsOspCompat()) == true,
233                         E_INVALID_ARG, " [%ls] is not compatible.", dirPath.GetPointer());
234
235         result r = E_SUCCESS;
236
237         if (_FileImpl::IsFileExist(dirPath) == false)
238         {
239                 r = GetLastResult();
240                 if (!IsFailed(r))
241                 {
242                         SysLog(NID_IO, "[E_FILE_NOT_FOUND] The directory path (%s) does not exist.", dirPath.GetPointer());
243                         r = E_FILE_NOT_FOUND;
244                 }
245                 else
246                 {
247                         SysPropagate(NID_IO, r);
248                 }
249                 return r;
250         }
251
252         unique_ptr<char[]> pDirPath(_StringConverter::CopyToCharArrayN(dirPath));
253         SysTryReturnResult(NID_IO, pDirPath != null, E_OUT_OF_MEMORY, "The memory is insufficient.");
254
255         char resolvedPath[PATH_MAX] = {0,};
256         if (realpath(pDirPath.get(), resolvedPath) == null)
257         {
258                 switch (errno)
259                 {
260                 case EACCES:
261                         r = E_ILLEGAL_ACCESS;
262                         break;
263                 case EINVAL:
264                         // fall through
265                 case ELOOP:
266                         // fall through
267                 case ENAMETOOLONG:
268                         // fall through
269                 case ENOTDIR:
270                         r = E_INVALID_ARG;
271                         break;
272                 case EIO:
273                         r = E_IO;
274                         break;
275                 case ENOENT:
276                         r = E_FILE_NOT_FOUND;
277                         break;
278                 default:
279                         r = E_SYSTEM;
280                         break;
281                 }
282
283                 SysLog(NID_IO, "[%s] Failed to produce canonical absolute path (%ls). errno: %d (%s)",
284                                 GetErrorMessage(r), dirPath.GetPointer(), errno, strerror(errno));
285
286                 return r;
287         }
288
289         int ret = rmdir(resolvedPath);
290         if (ret == -1)
291         {
292                 switch (errno)
293                 {
294                 case EACCES:
295                         // fall through
296                 case EPERM:
297                         r = E_ILLEGAL_ACCESS;
298                         break;
299                 case EINVAL:
300                         // fall through
301                 case ELOOP:
302                         // fall through
303                 case ENAMETOOLONG:
304                         // fall through
305                 case ENOTDIR:
306                         r = E_INVALID_ARG;
307                         break;
308                 case ENOENT:
309                         r = E_FILE_NOT_FOUND;
310                         break;
311                 case ENOMEM:
312                         r = E_OUT_OF_MEMORY;
313                         break;
314                 case ENOTEMPTY:
315                         if (recursive)
316                         {
317                                 r = RemoveRecursively(resolvedPath);
318                         }
319                         else
320                         {
321                                 r = E_FILE_ALREADY_EXIST;
322                         }
323                         break;
324                 default:
325                         r = E_SYSTEM;
326                         break;
327                 }
328                 if (IsFailed(r))
329                 {
330                         SysLog(NID_IO, "[%s] Failed to remove the directory path (%ls). errno: %d (%s)",
331                                         GetErrorMessage(r), dirPath.GetPointer(), errno, strerror(errno));
332                 }
333         }
334
335         return r;
336 }
337
338 result
339 _DirectoryImpl::Rename(const String& orgDirPath, const String& newDirPath)
340 {
341         result r = E_SUCCESS;
342         int ret = 0;
343
344         SysTryReturnResult(NID_IO, _FileImpl::VerifyFilePathCompatibility(orgDirPath, _AppInfo::IsOspCompat()) == true,
345                         E_INVALID_ARG, " [%ls] is not compatible.", orgDirPath.GetPointer());
346         SysTryReturnResult(NID_IO, _FileImpl::VerifyFilePathCompatibility(newDirPath, _AppInfo::IsOspCompat()) == true,
347                         E_INVALID_ARG, " [%ls] is not compatible.", newDirPath.GetPointer());
348
349         if (_FileImpl::IsFileExist(newDirPath))
350         {
351                 return E_FILE_ALREADY_EXIST;
352         }
353
354         unique_ptr<char[]> pOldpath(_StringConverter::CopyToCharArrayN(orgDirPath));
355         SysTryReturn(NID_IO, (null != pOldpath), GetLastResult(), GetLastResult(), "[%s] Invalid old file path.", GetErrorMessage(GetLastResult()));
356
357         unique_ptr<char[]> pNewpath(_StringConverter::CopyToCharArrayN(newDirPath));
358         SysTryReturn(NID_IO, (null != pNewpath), GetLastResult(), GetLastResult(), "[%s] Invalid new file path.", GetErrorMessage(GetLastResult()));
359
360         ret = rename(pOldpath.get(), pNewpath.get());
361         if (ret != 0)
362         {
363                 switch (errno)
364                 {
365                 case EBUSY:
366                 // fall through
367                 case EMLINK:
368                         r = E_IO;
369                         break;
370                 case ENOTEMPTY:
371                         r = E_FILE_ALREADY_EXIST;
372                         break;
373                 case EXDEV:
374                         r = E_INVALID_ARG;
375                         break;
376                 default:
377                         r = _NativeError::ConvertNativeErrorToResult(errno, true);
378                         break;
379                 }
380                 SysLogException(NID_IO, r, "[%s] Renaming the directory path (%s) is failed, errno: %d (%s)", GetErrorMessage(r), pOldpath.get(), errno, strerror(errno));
381         }
382
383         return r;
384 }
385
386 result
387 _DirectoryImpl::RemoveRecursively(char* pDirPath)
388 {
389         SysTryReturnResult(NID_IO, pDirPath != null, E_INVALID_ARG, "pDirPath is null.");
390
391         DIR* pDir = null;
392         result r = E_SUCCESS;
393
394         pDir = opendir(pDirPath);
395         if (pDir == null)
396         {
397                 r = _NativeError::ConvertNativeErrorToResult(errno, true);
398                 SysLog(NID_IO, "[%s] Failed to open directory (%s).", GetErrorMessage(r), pDirPath);
399                 goto CATCH;
400         }
401
402         while (1)
403         {
404                 struct dirent dirEnt;
405                 struct dirent* pDirEntResult = null;
406                 char *pDirEntryName = null;
407
408                 int ret = readdir_r(pDir, &dirEnt, &pDirEntResult);
409                 if (ret != 0)
410                 {
411                         r = _NativeError::ConvertNativeErrorToResult(errno, true);
412                         SysLog(NID_IO, "[%s] Failed to read sub-directories. errno: %d (%s)",
413                                         GetErrorMessage(r), errno, strerror(errno));
414                         goto CATCH;
415                 }
416
417                 // Remove all entries in dir. Delete the dir.
418                 if (pDirEntResult == null)
419                 {
420                         ret = rmdir(pDirPath);
421                         if (ret != 0)
422                         {
423                                 r = _NativeError::ConvertNativeErrorToResult(errno, true);
424                                 SysLog(NID_IO, "[%s] Failed to remove a directory. errno: %d (%s)",
425                                                 GetErrorMessage(r), errno, strerror(errno));
426                         }
427                         goto CATCH; // both success and fail
428                 }
429
430                 pDirEntryName = &dirEnt.d_name[0];
431
432                 // skip . and ..
433                 if ((*pDirEntryName == '.' && *(pDirEntryName + 1) == 0x00) || (*pDirEntryName == '.' && *(pDirEntryName + 1) == '.' && *(pDirEntryName + 2) == 0x00))
434                 {
435                         continue;
436                 }
437
438                 // if the path name has '/' at the end remove it.
439                 size_t len = strlen(pDirPath);
440                 if (pDirPath[len - 1] == '/')
441                 {
442                         pDirPath[len - 1] = '\0';
443                 }
444
445                 // read_dir return relative path. Turn it to absolute
446                 int parentDirLen = strlen(pDirPath);
447                 int currEntryLen = strlen(dirEnt.d_name);
448
449                 char absPath[parentDirLen + currEntryLen + 2]; // 2 - for '/' and '\0'
450
451                 strcpy(absPath, pDirPath);
452                 strcat(absPath, "/");
453                 strcat(absPath, dirEnt.d_name);
454
455                 struct stat64 statbuf;
456                 memset(&statbuf, 0, sizeof(struct stat64));
457                 if (lstat64(absPath, &statbuf) == -1)
458                 {
459                         r = _NativeError::ConvertNativeErrorToResult(errno, true);
460                         SysLog(NID_IO, "[%s] Failed to get file status. errno: %d (%s)",
461                                         GetErrorMessage(r), errno, strerror(errno));
462                         goto CATCH;
463                 }
464                 if (!S_ISLNK(statbuf.st_mode))
465                 {
466                         if (stat64(absPath, &statbuf) < 0)
467                         {
468                                 r = _NativeError::ConvertNativeErrorToResult(errno, true);
469                                 SysLog(NID_IO, "[%s] Failed to get file status. errno: %d (%s)",
470                                                 GetErrorMessage(r), errno, strerror(errno));
471                                 goto CATCH;
472                         }
473                 }
474
475                 if (S_ISDIR(statbuf.st_mode))
476                 {
477                         r = _DirectoryImpl::RemoveRecursively(absPath);
478                         if (IsFailed(r))
479                         {
480                                 goto CATCH;
481                         }
482                 }
483                 else
484                 {
485                         ret = remove(absPath);
486                         if (ret < 0)
487                         {
488                                 r = _NativeError::ConvertNativeErrorToResult(errno, true);
489                                 SysLog(NID_IO, "[%s] Failed to remove file (%s)", GetErrorMessage(r), absPath);
490                                 goto CATCH;
491                         }
492                 }
493         }
494
495         // fall through
496 CATCH:
497         if (pDir)
498         {
499                 closedir(pDir);
500         }
501
502         return r;
503 }
504
505 _DirectoryImpl*
506 _DirectoryImpl::GetInstance(Directory& directory)
507 {
508             return directory.__pDirectoryImpl;
509 }
510
511 const _DirectoryImpl*
512 _DirectoryImpl::GetInstance(const Directory& directory)
513 {
514             return directory.__pDirectoryImpl;
515 }
516
517 }} // Tizen::Io
518