Merge "Revert "Delete checking if strlen() is 0 in ConvertWcsToMbsN()"" 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                 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(), 0755);
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                 switch (errno)
365                 {
366                 case EBUSY:
367                 // fall through
368                 case EMLINK:
369                         r = E_IO;
370                         break;
371                 case ENOTEMPTY:
372                         r = E_FILE_ALREADY_EXIST;
373                         break;
374                 case EXDEV:
375                         r = E_INVALID_ARG;
376                         break;
377                 default:
378                         r = _NativeError::ConvertNativeErrorToResult(errno, true);
379                         break;
380                 }
381                 SysLogException(NID_IO, r, "[%s] Renaming the directory path (%s) is failed, errno: %d (%s)", GetErrorMessage(r), pOldpath.get(), errno, strerror(errno));
382         }
383
384         return r;
385 }
386
387 result
388 _DirectoryImpl::RemoveRecursively(char* pDirPath)
389 {
390         SysTryReturnResult(NID_IO, pDirPath != null, E_INVALID_ARG, "pDirPath is null.");
391
392         DIR* pDir = null;
393         result r = E_SUCCESS;
394
395         pDir = opendir(pDirPath);
396         if (pDir == null)
397         {
398                 r = _NativeError::ConvertNativeErrorToResult(errno, true);
399                 SysLog(NID_IO, "[%s] Failed to open directory (%s).", GetErrorMessage(r), pDirPath);
400                 goto CATCH;
401         }
402
403         while (1)
404         {
405                 struct dirent dirEnt;
406                 struct dirent* pDirEntResult = null;
407                 char *pDirEntryName = null;
408
409                 int ret = readdir_r(pDir, &dirEnt, &pDirEntResult);
410                 if (ret != 0)
411                 {
412                         r = _NativeError::ConvertNativeErrorToResult(errno, true);
413                         SysLog(NID_IO, "[%s] Failed to read sub-directories. errno: %d (%s)",
414                                         GetErrorMessage(r), errno, strerror(errno));
415                         goto CATCH;
416                 }
417
418                 // Remove all entries in dir. Delete the dir.
419                 if (pDirEntResult == null)
420                 {
421                         ret = rmdir(pDirPath);
422                         if (ret != 0)
423                         {
424                                 r = _NativeError::ConvertNativeErrorToResult(errno, true);
425                                 SysLog(NID_IO, "[%s] Failed to remove a directory. errno: %d (%s)",
426                                                 GetErrorMessage(r), errno, strerror(errno));
427                         }
428                         goto CATCH; // both success and fail
429                 }
430
431                 pDirEntryName = &dirEnt.d_name[0];
432
433                 // skip . and ..
434                 if ((*pDirEntryName == '.' && *(pDirEntryName + 1) == 0x00) || (*pDirEntryName == '.' && *(pDirEntryName + 1) == '.' && *(pDirEntryName + 2) == 0x00))
435                 {
436                         continue;
437                 }
438
439                 // if the path name has '/' at the end remove it.
440                 size_t len = strlen(pDirPath);
441                 if (pDirPath[len - 1] == '/')
442                 {
443                         pDirPath[len - 1] = '\0';
444                 }
445
446                 // read_dir return relative path. Turn it to absolute
447                 int parentDirLen = strlen(pDirPath);
448                 int currEntryLen = strlen(dirEnt.d_name);
449
450                 char absPath[parentDirLen + currEntryLen + 2]; // 2 - for '/' and '\0'
451
452                 strcpy(absPath, pDirPath);
453                 strcat(absPath, "/");
454                 strcat(absPath, dirEnt.d_name);
455
456                 struct stat64 statbuf;
457                 memset(&statbuf, 0, sizeof(struct stat64));
458                 if (lstat64(absPath, &statbuf) == -1)
459                 {
460                         r = _NativeError::ConvertNativeErrorToResult(errno, true);
461                         SysLog(NID_IO, "[%s] Failed to get file status. errno: %d (%s)",
462                                         GetErrorMessage(r), errno, strerror(errno));
463                         goto CATCH;
464                 }
465                 if (!S_ISLNK(statbuf.st_mode))
466                 {
467                         if (stat64(absPath, &statbuf) < 0)
468                         {
469                                 r = _NativeError::ConvertNativeErrorToResult(errno, true);
470                                 SysLog(NID_IO, "[%s] Failed to get file status. errno: %d (%s)",
471                                                 GetErrorMessage(r), errno, strerror(errno));
472                                 goto CATCH;
473                         }
474                 }
475
476                 if (S_ISDIR(statbuf.st_mode))
477                 {
478                         r = _DirectoryImpl::RemoveRecursively(absPath);
479                         if (IsFailed(r))
480                         {
481                                 goto CATCH;
482                         }
483                 }
484                 else
485                 {
486                         ret = remove(absPath);
487                         if (ret < 0)
488                         {
489                                 r = _NativeError::ConvertNativeErrorToResult(errno, true);
490                                 SysLog(NID_IO, "[%s] Failed to remove file (%s)", GetErrorMessage(r), absPath);
491                                 goto CATCH;
492                         }
493                 }
494         }
495
496         // fall through
497 CATCH:
498         if (pDir)
499         {
500                 closedir(pDir);
501         }
502
503         return r;
504 }
505
506 _DirectoryImpl*
507 _DirectoryImpl::GetInstance(Directory& directory)
508 {
509             return directory.__pDirectoryImpl;
510 }
511
512 const _DirectoryImpl*
513 _DirectoryImpl::GetInstance(const Directory& directory)
514 {
515             return directory.__pDirectoryImpl;
516 }
517
518 }} // Tizen::Io
519