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