Upstream version 9.37.197.0
[platform/framework/web/crosswalk.git] / src / third_party / libjingle / source / talk / base / unixfilesystem.cc
1 /*
2  * libjingle
3  * Copyright 2004--2006, Google Inc.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  *  1. Redistributions of source code must retain the above copyright notice,
9  *     this list of conditions and the following disclaimer.
10  *  2. Redistributions in binary form must reproduce the above copyright notice,
11  *     this list of conditions and the following disclaimer in the documentation
12  *     and/or other materials provided with the distribution.
13  *  3. The name of the author may not be used to endorse or promote products
14  *     derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19  * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27
28 #include "talk/base/unixfilesystem.h"
29
30 #include <errno.h>
31 #include <fcntl.h>
32 #include <stdlib.h>
33 #include <sys/stat.h>
34 #include <unistd.h>
35
36 #ifdef OSX
37 #include <Carbon/Carbon.h>
38 #include <IOKit/IOCFBundle.h>
39 #include <sys/statvfs.h>
40 #include "talk/base/macutils.h"
41 #endif  // OSX
42
43 #if defined(POSIX) && !defined(OSX)
44 #include <sys/types.h>
45 #if defined(ANDROID)
46 #include <sys/statfs.h>
47 #elif !defined(__native_client__)
48 #include <sys/statvfs.h>
49 #endif  //  !defined(__native_client__)
50 #include <limits.h>
51 #include <pwd.h>
52 #include <stdio.h>
53 #endif  // POSIX && !OSX
54
55 #if defined(LINUX)
56 #include <ctype.h>
57 #include <algorithm>
58 #endif
59
60 #if defined(__native_client__) && !defined(__GLIBC__)
61 #include <sys/syslimits.h>
62 #endif
63
64 #include "talk/base/fileutils.h"
65 #include "talk/base/pathutils.h"
66 #include "talk/base/stream.h"
67 #include "talk/base/stringutils.h"
68
69 #if defined(IOS)
70 // Defined in iosfilesystem.mm.  No header file to discourage use
71 // elsewhere; other places should use GetApp{Data,Temp}Folder() in
72 // this file.  Don't copy/paste.  I mean it.
73 char* IOSDataDirectory();
74 char* IOSTempDirectory();
75 void IOSAppName(talk_base::Pathname* path);
76 #endif
77
78 namespace talk_base {
79
80 #if !defined(ANDROID) && !defined(IOS)
81 char* UnixFilesystem::app_temp_path_ = NULL;
82 #else
83 char* UnixFilesystem::provided_app_data_folder_ = NULL;
84 char* UnixFilesystem::provided_app_temp_folder_ = NULL;
85
86 void UnixFilesystem::SetAppDataFolder(const std::string& folder) {
87   delete [] provided_app_data_folder_;
88   provided_app_data_folder_ = CopyString(folder);
89 }
90
91 void UnixFilesystem::SetAppTempFolder(const std::string& folder) {
92   delete [] provided_app_temp_folder_;
93   provided_app_temp_folder_ = CopyString(folder);
94 }
95 #endif
96
97 UnixFilesystem::UnixFilesystem() {
98 #if defined(IOS)
99   if (!provided_app_data_folder_)
100     provided_app_data_folder_ = IOSDataDirectory();
101   if (!provided_app_temp_folder_)
102     provided_app_temp_folder_ = IOSTempDirectory();
103 #endif
104 }
105
106 UnixFilesystem::~UnixFilesystem() {}
107
108 bool UnixFilesystem::CreateFolder(const Pathname &path, mode_t mode) {
109   std::string pathname(path.pathname());
110   int len = pathname.length();
111   if ((len == 0) || (pathname[len - 1] != '/'))
112     return false;
113
114   struct stat st;
115   int res = ::stat(pathname.c_str(), &st);
116   if (res == 0) {
117     // Something exists at this location, check if it is a directory
118     return S_ISDIR(st.st_mode) != 0;
119   } else if (errno != ENOENT) {
120     // Unexpected error
121     return false;
122   }
123
124   // Directory doesn't exist, look up one directory level
125   do {
126     --len;
127   } while ((len > 0) && (pathname[len - 1] != '/'));
128
129   if (!CreateFolder(Pathname(pathname.substr(0, len)), mode)) {
130     return false;
131   }
132
133   LOG(LS_INFO) << "Creating folder: " << pathname;
134   return (0 == ::mkdir(pathname.c_str(), mode));
135 }
136
137 bool UnixFilesystem::CreateFolder(const Pathname &path) {
138   return CreateFolder(path, 0755);
139 }
140
141 FileStream *UnixFilesystem::OpenFile(const Pathname &filename,
142                                      const std::string &mode) {
143   FileStream *fs = new FileStream();
144   if (fs && !fs->Open(filename.pathname().c_str(), mode.c_str(), NULL)) {
145     delete fs;
146     fs = NULL;
147   }
148   return fs;
149 }
150
151 bool UnixFilesystem::CreatePrivateFile(const Pathname &filename) {
152   int fd = open(filename.pathname().c_str(),
153                 O_RDWR | O_CREAT | O_EXCL,
154                 S_IRUSR | S_IWUSR);
155   if (fd < 0) {
156     LOG_ERR(LS_ERROR) << "open() failed.";
157     return false;
158   }
159   // Don't need to keep the file descriptor.
160   if (close(fd) < 0) {
161     LOG_ERR(LS_ERROR) << "close() failed.";
162     // Continue.
163   }
164   return true;
165 }
166
167 bool UnixFilesystem::DeleteFile(const Pathname &filename) {
168   LOG(LS_INFO) << "Deleting file:" << filename.pathname();
169
170   if (!IsFile(filename)) {
171     ASSERT(IsFile(filename));
172     return false;
173   }
174   return ::unlink(filename.pathname().c_str()) == 0;
175 }
176
177 bool UnixFilesystem::DeleteEmptyFolder(const Pathname &folder) {
178   LOG(LS_INFO) << "Deleting folder" << folder.pathname();
179
180   if (!IsFolder(folder)) {
181     ASSERT(IsFolder(folder));
182     return false;
183   }
184   std::string no_slash(folder.pathname(), 0, folder.pathname().length()-1);
185   return ::rmdir(no_slash.c_str()) == 0;
186 }
187
188 bool UnixFilesystem::GetTemporaryFolder(Pathname &pathname, bool create,
189                                         const std::string *append) {
190 #ifdef OSX
191   FSRef fr;
192   if (0 != FSFindFolder(kOnAppropriateDisk, kTemporaryFolderType,
193                         kCreateFolder, &fr))
194     return false;
195   unsigned char buffer[NAME_MAX+1];
196   if (0 != FSRefMakePath(&fr, buffer, ARRAY_SIZE(buffer)))
197     return false;
198   pathname.SetPathname(reinterpret_cast<char*>(buffer), "");
199 #elif defined(ANDROID) || defined(IOS)
200   ASSERT(provided_app_temp_folder_ != NULL);
201   pathname.SetPathname(provided_app_temp_folder_, "");
202 #else  // !OSX && !ANDROID
203   if (const char* tmpdir = getenv("TMPDIR")) {
204     pathname.SetPathname(tmpdir, "");
205   } else if (const char* tmp = getenv("TMP")) {
206     pathname.SetPathname(tmp, "");
207   } else {
208 #ifdef P_tmpdir
209     pathname.SetPathname(P_tmpdir, "");
210 #else  // !P_tmpdir
211     pathname.SetPathname("/tmp/", "");
212 #endif  // !P_tmpdir
213   }
214 #endif  // !OSX && !ANDROID
215   if (append) {
216     ASSERT(!append->empty());
217     pathname.AppendFolder(*append);
218   }
219   return !create || CreateFolder(pathname);
220 }
221
222 std::string UnixFilesystem::TempFilename(const Pathname &dir,
223                                          const std::string &prefix) {
224   int len = dir.pathname().size() + prefix.size() + 2 + 6;
225   char *tempname = new char[len];
226
227   snprintf(tempname, len, "%s/%sXXXXXX", dir.pathname().c_str(),
228            prefix.c_str());
229   int fd = ::mkstemp(tempname);
230   if (fd != -1)
231     ::close(fd);
232   std::string ret(tempname);
233   delete[] tempname;
234
235   return ret;
236 }
237
238 bool UnixFilesystem::MoveFile(const Pathname &old_path,
239                               const Pathname &new_path) {
240   if (!IsFile(old_path)) {
241     ASSERT(IsFile(old_path));
242     return false;
243   }
244   LOG(LS_VERBOSE) << "Moving " << old_path.pathname()
245                   << " to " << new_path.pathname();
246   if (rename(old_path.pathname().c_str(), new_path.pathname().c_str()) != 0) {
247     if (errno != EXDEV)
248       return false;
249     if (!CopyFile(old_path, new_path))
250       return false;
251     if (!DeleteFile(old_path))
252       return false;
253   }
254   return true;
255 }
256
257 bool UnixFilesystem::MoveFolder(const Pathname &old_path,
258                                 const Pathname &new_path) {
259   if (!IsFolder(old_path)) {
260     ASSERT(IsFolder(old_path));
261     return false;
262   }
263   LOG(LS_VERBOSE) << "Moving " << old_path.pathname()
264                   << " to " << new_path.pathname();
265   if (rename(old_path.pathname().c_str(), new_path.pathname().c_str()) != 0) {
266     if (errno != EXDEV)
267       return false;
268     if (!CopyFolder(old_path, new_path))
269       return false;
270     if (!DeleteFolderAndContents(old_path))
271       return false;
272   }
273   return true;
274 }
275
276 bool UnixFilesystem::IsFolder(const Pathname &path) {
277   struct stat st;
278   if (stat(path.pathname().c_str(), &st) < 0)
279     return false;
280   return S_ISDIR(st.st_mode);
281 }
282
283 bool UnixFilesystem::CopyFile(const Pathname &old_path,
284                               const Pathname &new_path) {
285   LOG(LS_VERBOSE) << "Copying " << old_path.pathname()
286                   << " to " << new_path.pathname();
287   char buf[256];
288   size_t len;
289
290   StreamInterface *source = OpenFile(old_path, "rb");
291   if (!source)
292     return false;
293
294   StreamInterface *dest = OpenFile(new_path, "wb");
295   if (!dest) {
296     delete source;
297     return false;
298   }
299
300   while (source->Read(buf, sizeof(buf), &len, NULL) == SR_SUCCESS)
301     dest->Write(buf, len, NULL, NULL);
302
303   delete source;
304   delete dest;
305   return true;
306 }
307
308 bool UnixFilesystem::IsTemporaryPath(const Pathname& pathname) {
309 #if defined(ANDROID) || defined(IOS)
310   ASSERT(provided_app_temp_folder_ != NULL);
311 #endif
312
313   const char* const kTempPrefixes[] = {
314 #if defined(ANDROID) || defined(IOS)
315     provided_app_temp_folder_,
316 #else
317     "/tmp/", "/var/tmp/",
318 #ifdef OSX
319     "/private/tmp/", "/private/var/tmp/", "/private/var/folders/",
320 #endif  // OSX
321 #endif  // ANDROID || IOS
322   };
323   for (size_t i = 0; i < ARRAY_SIZE(kTempPrefixes); ++i) {
324     if (0 == strncmp(pathname.pathname().c_str(), kTempPrefixes[i],
325                      strlen(kTempPrefixes[i])))
326       return true;
327   }
328   return false;
329 }
330
331 bool UnixFilesystem::IsFile(const Pathname& pathname) {
332   struct stat st;
333   int res = ::stat(pathname.pathname().c_str(), &st);
334   // Treat symlinks, named pipes, etc. all as files.
335   return res == 0 && !S_ISDIR(st.st_mode);
336 }
337
338 bool UnixFilesystem::IsAbsent(const Pathname& pathname) {
339   struct stat st;
340   int res = ::stat(pathname.pathname().c_str(), &st);
341   // Note: we specifically maintain ENOTDIR as an error, because that implies
342   // that you could not call CreateFolder(pathname).
343   return res != 0 && ENOENT == errno;
344 }
345
346 bool UnixFilesystem::GetFileSize(const Pathname& pathname, size_t *size) {
347   struct stat st;
348   if (::stat(pathname.pathname().c_str(), &st) != 0)
349     return false;
350   *size = st.st_size;
351   return true;
352 }
353
354 bool UnixFilesystem::GetFileTime(const Pathname& path, FileTimeType which,
355                                  time_t* time) {
356   struct stat st;
357   if (::stat(path.pathname().c_str(), &st) != 0)
358     return false;
359   switch (which) {
360   case FTT_CREATED:
361     *time = st.st_ctime;
362     break;
363   case FTT_MODIFIED:
364     *time = st.st_mtime;
365     break;
366   case FTT_ACCESSED:
367     *time = st.st_atime;
368     break;
369   default:
370     return false;
371   }
372   return true;
373 }
374
375 bool UnixFilesystem::GetAppPathname(Pathname* path) {
376 #ifdef OSX
377   ProcessSerialNumber psn = { 0, kCurrentProcess };
378   CFDictionaryRef procinfo = ProcessInformationCopyDictionary(&psn,
379       kProcessDictionaryIncludeAllInformationMask);
380   if (NULL == procinfo)
381     return false;
382   CFStringRef cfpath = (CFStringRef) CFDictionaryGetValue(procinfo,
383       kIOBundleExecutableKey);
384   std::string path8;
385   bool success = ToUtf8(cfpath, &path8);
386   CFRelease(procinfo);
387   if (success)
388     path->SetPathname(path8);
389   return success;
390 #elif defined(__native_client__)
391   return false;
392 #elif IOS
393   IOSAppName(path);
394   return true;
395 #else  // OSX
396   char buffer[PATH_MAX + 2];
397   ssize_t len = readlink("/proc/self/exe", buffer, ARRAY_SIZE(buffer) - 1);
398   if ((len <= 0) || (len == PATH_MAX + 1))
399     return false;
400   buffer[len] = '\0';
401   path->SetPathname(buffer);
402   return true;
403 #endif  // OSX
404 }
405
406 bool UnixFilesystem::GetAppDataFolder(Pathname* path, bool per_user) {
407   ASSERT(!organization_name_.empty());
408   ASSERT(!application_name_.empty());
409
410   // First get the base directory for app data.
411 #ifdef OSX
412   if (per_user) {
413     // Use ~/Library/Application Support/<orgname>/<appname>/
414     FSRef fr;
415     if (0 != FSFindFolder(kUserDomain, kApplicationSupportFolderType,
416                           kCreateFolder, &fr))
417       return false;
418     unsigned char buffer[NAME_MAX+1];
419     if (0 != FSRefMakePath(&fr, buffer, ARRAY_SIZE(buffer)))
420       return false;
421     path->SetPathname(reinterpret_cast<char*>(buffer), "");
422   } else {
423     // TODO
424     return false;
425   }
426 #elif defined(ANDROID) || defined(IOS)  // && !OSX
427   ASSERT(provided_app_data_folder_ != NULL);
428   path->SetPathname(provided_app_data_folder_, "");
429 #elif defined(LINUX)  // && !OSX && !defined(ANDROID) && !defined(IOS)
430   if (per_user) {
431     // We follow the recommendations in
432     // http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
433     // It specifies separate directories for data and config files, but
434     // GetAppDataFolder() does not distinguish. We just return the config dir
435     // path.
436     const char* xdg_config_home = getenv("XDG_CONFIG_HOME");
437     if (xdg_config_home) {
438       path->SetPathname(xdg_config_home, "");
439     } else {
440       // XDG says to default to $HOME/.config. We also support falling back to
441       // other synonyms for HOME if for some reason it is not defined.
442       const char* homedir;
443       if (const char* home = getenv("HOME")) {
444         homedir = home;
445       } else if (const char* dotdir = getenv("DOTDIR")) {
446         homedir = dotdir;
447       } else if (passwd* pw = getpwuid(geteuid())) {
448         homedir = pw->pw_dir;
449       } else {
450         return false;
451       }
452       path->SetPathname(homedir, "");
453       path->AppendFolder(".config");
454     }
455   } else {
456     // XDG does not define a standard directory for writable global data. Let's
457     // just use this.
458     path->SetPathname("/var/cache/", "");
459   }
460 #endif  // !OSX && !defined(ANDROID) && !defined(LINUX)
461
462   // Now add on a sub-path for our app.
463 #if defined(OSX) || defined(ANDROID) || defined(IOS)
464   path->AppendFolder(organization_name_);
465   path->AppendFolder(application_name_);
466 #elif defined(LINUX)
467   // XDG says to use a single directory level, so we concatenate the org and app
468   // name with a hyphen. We also do the Linuxy thing and convert to all
469   // lowercase with no spaces.
470   std::string subdir(organization_name_);
471   subdir.append("-");
472   subdir.append(application_name_);
473   replace_substrs(" ", 1, "", 0, &subdir);
474   std::transform(subdir.begin(), subdir.end(), subdir.begin(), ::tolower);
475   path->AppendFolder(subdir);
476 #endif
477   if (!CreateFolder(*path, 0700)) {
478     return false;
479   }
480 #if !defined(__native_client__)
481   // If the folder already exists, it may have the wrong mode or be owned by
482   // someone else, both of which are security problems. Setting the mode
483   // avoids both issues since it will fail if the path is not owned by us.
484   if (0 != ::chmod(path->pathname().c_str(), 0700)) {
485     LOG_ERR(LS_ERROR) << "Can't set mode on " << path;
486     return false;
487   }
488 #endif
489   return true;
490 }
491
492 bool UnixFilesystem::GetAppTempFolder(Pathname* path) {
493 #if defined(ANDROID) || defined(IOS)
494   ASSERT(provided_app_temp_folder_ != NULL);
495   path->SetPathname(provided_app_temp_folder_);
496   return true;
497 #else
498   ASSERT(!application_name_.empty());
499   // TODO: Consider whether we are worried about thread safety.
500   if (app_temp_path_ != NULL && strlen(app_temp_path_) > 0) {
501     path->SetPathname(app_temp_path_);
502     return true;
503   }
504
505   // Create a random directory as /tmp/<appname>-<pid>-<timestamp>
506   char buffer[128];
507   sprintfn(buffer, ARRAY_SIZE(buffer), "-%d-%d",
508            static_cast<int>(getpid()),
509            static_cast<int>(time(0)));
510   std::string folder(application_name_);
511   folder.append(buffer);
512   if (!GetTemporaryFolder(*path, true, &folder))
513     return false;
514
515   delete [] app_temp_path_;
516   app_temp_path_ = CopyString(path->pathname());
517   // TODO: atexit(DeleteFolderAndContents(app_temp_path_));
518   return true;
519 #endif
520 }
521
522 bool UnixFilesystem::GetDiskFreeSpace(const Pathname& path, int64 *freebytes) {
523 #ifdef __native_client__
524   return false;
525 #else  // __native_client__
526   ASSERT(NULL != freebytes);
527   // TODO: Consider making relative paths absolute using cwd.
528   // TODO: When popping off a symlink, push back on the components of the
529   // symlink, so we don't jump out of the target disk inadvertently.
530   Pathname existing_path(path.folder(), "");
531   while (!existing_path.folder().empty() && IsAbsent(existing_path)) {
532     existing_path.SetFolder(existing_path.parent_folder());
533   }
534 #ifdef ANDROID
535   struct statfs vfs;
536   memset(&vfs, 0, sizeof(vfs));
537   if (0 != statfs(existing_path.pathname().c_str(), &vfs))
538     return false;
539 #else
540   struct statvfs vfs;
541   memset(&vfs, 0, sizeof(vfs));
542   if (0 != statvfs(existing_path.pathname().c_str(), &vfs))
543     return false;
544 #endif  // ANDROID
545 #if defined(LINUX) || defined(ANDROID)
546   *freebytes = static_cast<int64>(vfs.f_bsize) * vfs.f_bavail;
547 #elif defined(OSX) || defined(IOS)
548   *freebytes = static_cast<int64>(vfs.f_frsize) * vfs.f_bavail;
549 #endif
550
551   return true;
552 #endif  // !__native_client__
553 }
554
555 Pathname UnixFilesystem::GetCurrentDirectory() {
556   Pathname cwd;
557   char buffer[PATH_MAX];
558   char *path = getcwd(buffer, PATH_MAX);
559
560   if (!path) {
561     LOG_ERR(LS_ERROR) << "getcwd() failed";
562     return cwd;  // returns empty pathname
563   }
564   cwd.SetFolder(std::string(path));
565
566   return cwd;
567 }
568
569 char* UnixFilesystem::CopyString(const std::string& str) {
570   size_t size = str.length() + 1;
571
572   char* buf = new char[size];
573   if (!buf) {
574     return NULL;
575   }
576
577   strcpyn(buf, size, str.c_str());
578   return buf;
579 }
580
581 }  // namespace talk_base
582
583 #if defined(__native_client__)
584 extern "C" int __attribute__((weak))
585 link(const char* oldpath, const char* newpath) {
586   errno = EACCES;
587   return -1;
588 }
589 #endif