sync with tizen_2.0
[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                 SysLog(NID_IO, "[%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
105         return r;
106 }
107
108 DirEnumerator*
109 _DirectoryImpl::ReadN(void)
110 {
111         SysAssertf(__pDir != null, "Not yet constructed. Construct() should be called before use.\n");
112
113         unique_ptr<DirEnumerator> pDirEnum(_DirEnumeratorImpl::CreateDirEnumeratorInstanceN(__dirPath));
114         SysTryReturn(NID_IO, pDirEnum != null, null, E_OUT_OF_MEMORY,
115                            "[E_OUT_OF_MEMORY] The memory is insufficient.");
116
117         return pDirEnum.release();
118 }
119
120 result
121 _DirectoryImpl::Create(const String& dirPath, bool createParentDirsToo)
122 {
123         result r = E_SUCCESS;
124         int ret = 0;
125
126         SysTryReturnResult(NID_IO, _FileImpl::VerifyFilePathCompatibility(dirPath, _AppInfo::IsOspCompat()) == true,
127                         E_INVALID_ARG, " [%ls] is not compatible.", dirPath.GetPointer());
128
129         unique_ptr<char[]> pTempDirPath(_StringConverter::CopyToCharArrayN(dirPath));
130         SysTryReturn(NID_IO, pTempDirPath != null, GetLastResult(), GetLastResult(),
131                         "[%s] Invalid dir path.", GetErrorMessage(GetLastResult()));
132
133         unique_ptr<char[]> pDirPath(new (std::nothrow) char[strlen(pTempDirPath.get()) + 2]);
134         SysTryReturnResult(NID_IO, pDirPath != null, E_OUT_OF_MEMORY, "The memory is insufficient.");
135
136         strcpy(pDirPath.get(), pTempDirPath.get());
137         pDirPath[strlen(pTempDirPath.get())] = '/';
138         pDirPath[strlen(pTempDirPath.get()) + 1] = '\0';
139
140         // By the time we are here.. we assume we have a legal path to create
141         // the below logic works under the assumption that the path name does not end with '/'
142         ret = mkdir(pDirPath.get(), S_IRUSR | S_IWUSR | S_IXUSR);
143         if (ret == -1)
144         {
145                 // if parent directory does not exist and if we are asked to create them
146                 if ((errno == ENOENT) && (createParentDirsToo == true))
147                 {
148                         // move till we find the directory which does not exist
149                         char* pPos = pDirPath.get();
150
151                         // if the dir path starts with '/', skip it
152                         if (*pPos == '/')
153                         {
154                                 pPos++;
155                         }
156
157                         // scan for the next path seperator ie., '/'.
158                         while (1)
159                         {
160                                 if (pPos && *pPos == '/')
161                                 {
162                                         *pPos = '\0';
163                                         if (access(pDirPath.get(), F_OK) != 0)
164                                         {
165                                                 break;
166                                         }
167                                         *pPos = '/';
168                                 }
169                                 pPos++;
170                         }
171
172                         // found a dir which is not present and realDirPath has that name
173                         while (1)
174                         {
175                                 ret = mkdir(pDirPath.get(), S_IRUSR | S_IWUSR | S_IXUSR);
176                                 if (ret == -1 && errno != EEXIST)
177                                 {
178                                         SysLog(NID_IO, "mkdir() failed. errno: %d (%s), path: %ls",
179                                                         errno, strerror(errno), dirPath.GetPointer());
180                                         r = _NativeError::ConvertNativeErrorToResult(errno, true);
181                                         goto CATCH;
182                                 }
183
184                                 // find for the next dir path
185                                 *pPos = '/';
186
187                                 while (pPos && *pPos == '/')
188                                 {
189                                         pPos++; // skip all occurances of consecutive '/' from current position
190                                 }
191
192                                 if (pPos == null)
193                                 {
194                                         goto CATCH; // E_SUCCESS
195                                 }
196
197                                 pPos = strchr(pPos, '/');
198                                 if (pPos != null)
199                                 {
200                                         *pPos = '\0';
201                                 }
202                                 else
203                                 {
204                                         goto CATCH; // E_SUCCESS
205                                 }
206                         }
207                 }
208                 else
209                 {
210                         if (errno == ENAMETOOLONG)
211                         {
212                                 r = E_INVALID_ARG;
213                         }
214                         else
215                         {
216                                 r = _NativeError::ConvertNativeErrorToResult(errno, true);
217                         }
218                         SysLog(NID_IO, "[%s] Failed to create directory (%s) errno: %d (%s).",
219                                         GetErrorMessage(r), dirPath.GetPointer(), errno, strerror(errno));
220                         goto CATCH;
221                 }
222         }
223
224         // fall through
225 CATCH:
226
227         return r;
228 }
229
230 result
231 _DirectoryImpl::Remove(const String& dirPath, bool recursive)
232 {
233         SysTryReturnResult(NID_IO, _FileImpl::VerifyFilePathCompatibility(dirPath, _AppInfo::IsOspCompat()) == true,
234                         E_INVALID_ARG, " [%ls] is not compatible.", dirPath.GetPointer());
235
236         result r = E_SUCCESS;
237
238         if (_FileImpl::IsFileExist(dirPath) == false)
239         {
240                 r = GetLastResult();
241                 if (!IsFailed(r))
242                 {
243                         SysLog(NID_IO, "[E_FILE_NOT_FOUND] The directory path (%s) does not exist.", dirPath.GetPointer());
244                         r = E_FILE_NOT_FOUND;
245                 }
246                 else
247                 {
248                         SysPropagate(NID_IO, r);
249                 }
250                 return r;
251         }
252
253         unique_ptr<char[]> pDirPath(_StringConverter::CopyToCharArrayN(dirPath));
254         SysTryReturnResult(NID_IO, pDirPath != null, E_OUT_OF_MEMORY, "The memory is insufficient.");
255
256         char resolvedPath[PATH_MAX] = {0,};
257         if (realpath(pDirPath.get(), resolvedPath) == null)
258         {
259                 switch (errno)
260                 {
261                 case EACCES:
262                         r = E_ILLEGAL_ACCESS;
263                         break;
264                 case EINVAL:
265                         // fall through
266                 case ELOOP:
267                         // fall through
268                 case ENAMETOOLONG:
269                         // fall through
270                 case ENOTDIR:
271                         r = E_INVALID_ARG;
272                         break;
273                 case EIO:
274                         r = E_IO;
275                         break;
276                 case ENOENT:
277                         r = E_FILE_NOT_FOUND;
278                         break;
279                 default:
280                         r = E_SYSTEM;
281                         break;
282                 }
283
284                 SysLog(NID_IO, "[%s] Failed to produce canonical absolute path (%ls). errno: %d (%s)",
285                                 GetErrorMessage(r), dirPath.GetPointer(), errno, strerror(errno));
286
287                 return r;
288         }
289
290         int ret = rmdir(resolvedPath);
291         if (ret == -1)
292         {
293                 switch (errno)
294                 {
295                 case EACCES:
296                         // fall through
297                 case EPERM:
298                         r = E_ILLEGAL_ACCESS;
299                         break;
300                 case EINVAL:
301                         // fall through
302                 case ELOOP:
303                         // fall through
304                 case ENAMETOOLONG:
305                         // fall through
306                 case ENOTDIR:
307                         r = E_INVALID_ARG;
308                         break;
309                 case ENOENT:
310                         r = E_FILE_NOT_FOUND;
311                         break;
312                 case ENOMEM:
313                         r = E_OUT_OF_MEMORY;
314                         break;
315                 case ENOTEMPTY:
316                         if (recursive)
317                         {
318                                 r = RemoveRecursively(resolvedPath);
319                         }
320                         else
321                         {
322                                 r = E_FILE_ALREADY_EXIST;
323                         }
324                         break;
325                 default:
326                         r = E_SYSTEM;
327                         break;
328                 }
329                 if (IsFailed(r))
330                 {
331                         SysLog(NID_IO, "[%s] Failed to remove the directory path (%ls). errno: %d (%s)",
332                                         GetErrorMessage(r), dirPath.GetPointer(), errno, strerror(errno));
333                 }
334         }
335
336         return r;
337 }
338
339 result
340 _DirectoryImpl::Rename(const String& orgDirPath, const String& newDirPath)
341 {
342         result r = E_SUCCESS;
343         int ret = 0;
344
345         SysTryReturnResult(NID_IO, _FileImpl::VerifyFilePathCompatibility(orgDirPath, _AppInfo::IsOspCompat()) == true,
346                         E_INVALID_ARG, " [%ls] is not compatible.", orgDirPath.GetPointer());
347         SysTryReturnResult(NID_IO, _FileImpl::VerifyFilePathCompatibility(newDirPath, _AppInfo::IsOspCompat()) == true,
348                         E_INVALID_ARG, " [%ls] is not compatible.", newDirPath.GetPointer());
349
350         if (_FileImpl::IsFileExist(newDirPath))
351         {
352                 return E_FILE_ALREADY_EXIST;
353         }
354
355         unique_ptr<char[]> pOldpath(_StringConverter::CopyToCharArrayN(orgDirPath));
356         SysTryReturn(NID_IO, (null != pOldpath), GetLastResult(), GetLastResult(), "[%s] Invalid old file path.", GetErrorMessage(GetLastResult()));
357
358         unique_ptr<char[]> pNewpath(_StringConverter::CopyToCharArrayN(newDirPath));
359         SysTryReturn(NID_IO, (null != pNewpath), GetLastResult(), GetLastResult(), "[%s] Invalid new file path.", GetErrorMessage(GetLastResult()));
360
361         ret = rename(pOldpath.get(), pNewpath.get());
362         if (ret != 0)
363         {
364                 r = _NativeError::ConvertNativeErrorToResult(errno, true);
365                 SysLog(NID_IO, "errno[%d], [%s] could not rename dirpath [%s].", errno, GetErrorMessage(r), pOldpath.get());
366         }
367
368         return r;
369 }
370
371 result
372 _DirectoryImpl::RemoveRecursively(char* pDirPath)
373 {
374         SysTryReturnResult(NID_IO, pDirPath != null, E_INVALID_ARG, "pDirPath is null.");
375
376         DIR* pDir = null;
377         result r = E_SUCCESS;
378
379         pDir = opendir(pDirPath);
380         if (pDir == null)
381         {
382                 r = _NativeError::ConvertNativeErrorToResult(errno, true);
383                 SysLog(NID_IO, "[%s] Failed to open directory (%s).", GetErrorMessage(r), pDirPath);
384                 goto CATCH;
385         }
386
387         while (1)
388         {
389                 struct dirent dirEnt;
390                 struct dirent* pDirEntResult = null;
391                 char *pDirEntryName = null;
392
393                 int ret = readdir_r(pDir, &dirEnt, &pDirEntResult);
394                 if (ret != 0)
395                 {
396                         r = _NativeError::ConvertNativeErrorToResult(errno, true);
397                         SysLog(NID_IO, "[%s] Failed to read sub-directories. errno: %d (%s)",
398                                         GetErrorMessage(r), errno, strerror(errno));
399                         goto CATCH;
400                 }
401
402                 // Remove all entries in dir. Delete the dir.
403                 if (pDirEntResult == null)
404                 {
405                         ret = rmdir(pDirPath);
406                         if (ret != 0)
407                         {
408                                 r = _NativeError::ConvertNativeErrorToResult(errno, true);
409                                 SysLog(NID_IO, "[%s] Failed to remove a directory. errno: %d (%s)",
410                                                 GetErrorMessage(r), errno, strerror(errno));
411                         }
412                         goto CATCH; // both success and fail
413                 }
414
415                 pDirEntryName = &dirEnt.d_name[0];
416
417                 // skip . and ..
418                 if ((*pDirEntryName == '.' && *(pDirEntryName + 1) == 0x00) || (*pDirEntryName == '.' && *(pDirEntryName + 1) == '.' && *(pDirEntryName + 2) == 0x00))
419                 {
420                         continue;
421                 }
422
423                 // if the path name has '/' at the end remove it.
424                 size_t len = strlen(pDirPath);
425                 if (pDirPath[len - 1] == '/')
426                 {
427                         pDirPath[len - 1] = '\0';
428                 }
429
430                 // read_dir return relative path. Turn it to absolute
431                 int parentDirLen = strlen(pDirPath);
432                 int currEntryLen = strlen(dirEnt.d_name);
433
434                 char absPath[parentDirLen + currEntryLen + 2]; // 2 - for '/' and '\0'
435
436                 strcpy(absPath, pDirPath);
437                 strcat(absPath, "/");
438                 strcat(absPath, dirEnt.d_name);
439                 //SysLog(NID_IO, "entry name: %s", absPath);
440
441                 struct stat statbuf;
442                 memset(&statbuf, 0, sizeof(struct stat));
443                 if (lstat(absPath, &statbuf) == -1)
444                 {
445                         r = _NativeError::ConvertNativeErrorToResult(errno, true);
446                         SysLog(NID_IO, "[%s] Failed to get file status. errno: %d (%s)",
447                                         GetErrorMessage(r), errno, strerror(errno));
448                         goto CATCH;
449                 }
450                 if (!S_ISLNK(statbuf.st_mode))
451                 {
452                         if (stat(absPath, &statbuf) < 0)
453                         {
454                                 r = _NativeError::ConvertNativeErrorToResult(errno, true);
455                                 SysLog(NID_IO, "[%s] Failed to get file status. errno: %d (%s)",
456                                                 GetErrorMessage(r), errno, strerror(errno));
457                                 goto CATCH;
458                         }
459                 }
460
461                 if (S_ISDIR(statbuf.st_mode))
462                 {
463                         r = _DirectoryImpl::RemoveRecursively(absPath);
464                         if (IsFailed(r))
465                         {
466                                 goto CATCH;
467                         }
468                 }
469                 else
470                 {
471                         ret = remove(absPath);
472                         if (ret < 0)
473                         {
474                                 r = _NativeError::ConvertNativeErrorToResult(errno, true);
475                                 SysLog(NID_IO, "[%s] Failed to remove file (%s)", GetErrorMessage(r), absPath);
476                                 goto CATCH;
477                         }
478                 }
479         }
480
481         // fall through
482 CATCH:
483         if (pDir)
484         {
485                 closedir(pDir);
486         }
487
488         return r;
489 }
490
491 _DirectoryImpl*
492 _DirectoryImpl::GetInstance(Directory& directory)
493 {
494             return directory.__pDirectoryImpl;
495 }
496
497 const _DirectoryImpl*
498 _DirectoryImpl::GetInstance(const Directory& directory)
499 {
500             return directory.__pDirectoryImpl;
501 }
502
503 }} // Tizen::Io
504