- add third_party src.
[platform/framework/web/crosswalk.git] / src / third_party / libjingle / source / talk / base / win32filesystem.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/win32filesystem.h"
29
30 #include "talk/base/win32.h"
31 #include <shellapi.h>
32 #include <shlobj.h>
33 #include <tchar.h>
34
35 #include "talk/base/fileutils.h"
36 #include "talk/base/pathutils.h"
37 #include "talk/base/scoped_ptr.h"
38 #include "talk/base/stream.h"
39 #include "talk/base/stringutils.h"
40
41 // In several places in this file, we test the integrity level of the process
42 // before calling GetLongPathName. We do this because calling GetLongPathName
43 // when running under protected mode IE (a low integrity process) can result in
44 // a virtualized path being returned, which is wrong if you only plan to read.
45 // TODO: Waiting to hear back from IE team on whether this is the
46 // best approach; IEIsProtectedModeProcess is another possible solution.
47
48 namespace talk_base {
49
50 bool Win32Filesystem::CreateFolder(const Pathname &pathname) {
51   if (pathname.pathname().empty() || !pathname.filename().empty())
52     return false;
53
54   std::wstring path16;
55   if (!Utf8ToWindowsFilename(pathname.pathname(), &path16))
56     return false;
57
58   DWORD res = ::GetFileAttributes(path16.c_str());
59   if (res != INVALID_FILE_ATTRIBUTES) {
60     // Something exists at this location, check if it is a directory
61     return ((res & FILE_ATTRIBUTE_DIRECTORY) != 0);
62   } else if ((GetLastError() != ERROR_FILE_NOT_FOUND)
63               && (GetLastError() != ERROR_PATH_NOT_FOUND)) {
64     // Unexpected error
65     return false;
66   }
67
68   // Directory doesn't exist, look up one directory level
69   if (!pathname.parent_folder().empty()) {
70     Pathname parent(pathname);
71     parent.SetFolder(pathname.parent_folder());
72     if (!CreateFolder(parent)) {
73       return false;
74     }
75   }
76
77   return (::CreateDirectory(path16.c_str(), NULL) != 0);
78 }
79
80 FileStream *Win32Filesystem::OpenFile(const Pathname &filename,
81                                       const std::string &mode) {
82   FileStream *fs = new FileStream();
83   if (fs && !fs->Open(filename.pathname().c_str(), mode.c_str(), NULL)) {
84     delete fs;
85     fs = NULL;
86   }
87   return fs;
88 }
89
90 bool Win32Filesystem::CreatePrivateFile(const Pathname &filename) {
91   // To make the file private to the current user, we first must construct a
92   // SECURITY_DESCRIPTOR specifying an ACL. This code is mostly based upon
93   // http://msdn.microsoft.com/en-us/library/ms707085%28VS.85%29.aspx
94
95   // Get the current process token.
96   HANDLE process_token = INVALID_HANDLE_VALUE;
97   if (!::OpenProcessToken(::GetCurrentProcess(),
98                           TOKEN_QUERY,
99                           &process_token)) {
100     LOG_ERR(LS_ERROR) << "OpenProcessToken() failed";
101     return false;
102   }
103
104   // Get the size of its TOKEN_USER structure. Return value is not checked
105   // because we expect it to fail.
106   DWORD token_user_size = 0;
107   (void)::GetTokenInformation(process_token,
108                               TokenUser,
109                               NULL,
110                               0,
111                               &token_user_size);
112
113   // Get the TOKEN_USER structure.
114   scoped_ptr<char[]> token_user_bytes(new char[token_user_size]);
115   PTOKEN_USER token_user = reinterpret_cast<PTOKEN_USER>(
116       token_user_bytes.get());
117   memset(token_user, 0, token_user_size);
118   BOOL success = ::GetTokenInformation(process_token,
119                                        TokenUser,
120                                        token_user,
121                                        token_user_size,
122                                        &token_user_size);
123   // We're now done with this.
124   ::CloseHandle(process_token);
125   if (!success) {
126     LOG_ERR(LS_ERROR) << "GetTokenInformation() failed";
127     return false;
128   }
129
130   if (!IsValidSid(token_user->User.Sid)) {
131     LOG_ERR(LS_ERROR) << "Current process has invalid user SID";
132     return false;
133   }
134
135   // Compute size needed for an ACL that allows access to just this user.
136   int acl_size = sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD) +
137       GetLengthSid(token_user->User.Sid);
138
139   // Allocate it.
140   scoped_ptr<char[]> acl_bytes(new char[acl_size]);
141   PACL acl = reinterpret_cast<PACL>(acl_bytes.get());
142   memset(acl, 0, acl_size);
143   if (!::InitializeAcl(acl, acl_size, ACL_REVISION)) {
144     LOG_ERR(LS_ERROR) << "InitializeAcl() failed";
145     return false;
146   }
147
148   // Allow access to only the current user.
149   if (!::AddAccessAllowedAce(acl,
150                              ACL_REVISION,
151                              GENERIC_READ | GENERIC_WRITE | STANDARD_RIGHTS_ALL,
152                              token_user->User.Sid)) {
153     LOG_ERR(LS_ERROR) << "AddAccessAllowedAce() failed";
154     return false;
155   }
156
157   // Now make the security descriptor.
158   SECURITY_DESCRIPTOR security_descriptor;
159   if (!::InitializeSecurityDescriptor(&security_descriptor,
160                                       SECURITY_DESCRIPTOR_REVISION)) {
161     LOG_ERR(LS_ERROR) << "InitializeSecurityDescriptor() failed";
162     return false;
163   }
164
165   // Put the ACL in it.
166   if (!::SetSecurityDescriptorDacl(&security_descriptor,
167                                    TRUE,
168                                    acl,
169                                    FALSE)) {
170     LOG_ERR(LS_ERROR) << "SetSecurityDescriptorDacl() failed";
171     return false;
172   }
173
174   // Finally create the file.
175   SECURITY_ATTRIBUTES security_attributes;
176   security_attributes.nLength = sizeof(security_attributes);
177   security_attributes.lpSecurityDescriptor = &security_descriptor;
178   security_attributes.bInheritHandle = FALSE;
179   HANDLE handle = ::CreateFile(
180       ToUtf16(filename.pathname()).c_str(),
181       GENERIC_READ | GENERIC_WRITE,
182       FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
183       &security_attributes,
184       CREATE_NEW,
185       0,
186       NULL);
187   if (INVALID_HANDLE_VALUE == handle) {
188     LOG_ERR(LS_ERROR) << "CreateFile() failed";
189     return false;
190   }
191   if (!::CloseHandle(handle)) {
192     LOG_ERR(LS_ERROR) << "CloseFile() failed";
193     // Continue.
194   }
195   return true;
196 }
197
198 bool Win32Filesystem::DeleteFile(const Pathname &filename) {
199   LOG(LS_INFO) << "Deleting file " << filename.pathname();
200   if (!IsFile(filename)) {
201     ASSERT(IsFile(filename));
202     return false;
203   }
204   return ::DeleteFile(ToUtf16(filename.pathname()).c_str()) != 0;
205 }
206
207 bool Win32Filesystem::DeleteEmptyFolder(const Pathname &folder) {
208   LOG(LS_INFO) << "Deleting folder " << folder.pathname();
209
210   std::string no_slash(folder.pathname(), 0, folder.pathname().length()-1);
211   return ::RemoveDirectory(ToUtf16(no_slash).c_str()) != 0;
212 }
213
214 bool Win32Filesystem::GetTemporaryFolder(Pathname &pathname, bool create,
215                                          const std::string *append) {
216   wchar_t buffer[MAX_PATH + 1];
217   if (!::GetTempPath(ARRAY_SIZE(buffer), buffer))
218     return false;
219   if (!IsCurrentProcessLowIntegrity() &&
220       !::GetLongPathName(buffer, buffer, ARRAY_SIZE(buffer)))
221     return false;
222   size_t len = strlen(buffer);
223   if ((len > 0) && (buffer[len-1] != '\\')) {
224     len += strcpyn(buffer + len, ARRAY_SIZE(buffer) - len, L"\\");
225   }
226   if (len >= ARRAY_SIZE(buffer) - 1)
227     return false;
228   pathname.clear();
229   pathname.SetFolder(ToUtf8(buffer));
230   if (append != NULL) {
231     ASSERT(!append->empty());
232     pathname.AppendFolder(*append);
233   }
234   return !create || CreateFolder(pathname);
235 }
236
237 std::string Win32Filesystem::TempFilename(const Pathname &dir,
238                                           const std::string &prefix) {
239   wchar_t filename[MAX_PATH];
240   if (::GetTempFileName(ToUtf16(dir.pathname()).c_str(),
241                         ToUtf16(prefix).c_str(), 0, filename) != 0)
242     return ToUtf8(filename);
243   ASSERT(false);
244   return "";
245 }
246
247 bool Win32Filesystem::MoveFile(const Pathname &old_path,
248                                const Pathname &new_path) {
249   if (!IsFile(old_path)) {
250     ASSERT(IsFile(old_path));
251     return false;
252   }
253   LOG(LS_INFO) << "Moving " << old_path.pathname()
254                << " to " << new_path.pathname();
255   return ::MoveFile(ToUtf16(old_path.pathname()).c_str(),
256                     ToUtf16(new_path.pathname()).c_str()) != 0;
257 }
258
259 bool Win32Filesystem::MoveFolder(const Pathname &old_path,
260                                  const Pathname &new_path) {
261   if (!IsFolder(old_path)) {
262     ASSERT(IsFolder(old_path));
263     return false;
264   }
265   LOG(LS_INFO) << "Moving " << old_path.pathname()
266                << " to " << new_path.pathname();
267   if (::MoveFile(ToUtf16(old_path.pathname()).c_str(),
268                ToUtf16(new_path.pathname()).c_str()) == 0) {
269     if (::GetLastError() != ERROR_NOT_SAME_DEVICE) {
270       LOG_GLE(LS_ERROR) << "Failed to move file";
271       return false;
272     }
273     if (!CopyFolder(old_path, new_path))
274       return false;
275     if (!DeleteFolderAndContents(old_path))
276       return false;
277   }
278   return true;
279 }
280
281 bool Win32Filesystem::IsFolder(const Pathname &path) {
282   WIN32_FILE_ATTRIBUTE_DATA data = {0};
283   if (0 == ::GetFileAttributesEx(ToUtf16(path.pathname()).c_str(),
284                                  GetFileExInfoStandard, &data))
285     return false;
286   return (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ==
287       FILE_ATTRIBUTE_DIRECTORY;
288 }
289
290 bool Win32Filesystem::IsFile(const Pathname &path) {
291   WIN32_FILE_ATTRIBUTE_DATA data = {0};
292   if (0 == ::GetFileAttributesEx(ToUtf16(path.pathname()).c_str(),
293                                  GetFileExInfoStandard, &data))
294     return false;
295   return (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0;
296 }
297
298 bool Win32Filesystem::IsAbsent(const Pathname& path) {
299   WIN32_FILE_ATTRIBUTE_DATA data = {0};
300   if (0 != ::GetFileAttributesEx(ToUtf16(path.pathname()).c_str(),
301                                  GetFileExInfoStandard, &data))
302     return false;
303   DWORD err = ::GetLastError();
304   return (ERROR_FILE_NOT_FOUND == err || ERROR_PATH_NOT_FOUND == err);
305 }
306
307 bool Win32Filesystem::CopyFile(const Pathname &old_path,
308                                const Pathname &new_path) {
309   return ::CopyFile(ToUtf16(old_path.pathname()).c_str(),
310                     ToUtf16(new_path.pathname()).c_str(), TRUE) != 0;
311 }
312
313 bool Win32Filesystem::IsTemporaryPath(const Pathname& pathname) {
314   TCHAR buffer[MAX_PATH + 1];
315   if (!::GetTempPath(ARRAY_SIZE(buffer), buffer))
316     return false;
317   if (!IsCurrentProcessLowIntegrity() &&
318       !::GetLongPathName(buffer, buffer, ARRAY_SIZE(buffer)))
319     return false;
320   return (::strnicmp(ToUtf16(pathname.pathname()).c_str(),
321                      buffer, strlen(buffer)) == 0);
322 }
323
324 bool Win32Filesystem::GetFileSize(const Pathname &pathname, size_t *size) {
325   WIN32_FILE_ATTRIBUTE_DATA data = {0};
326   if (::GetFileAttributesEx(ToUtf16(pathname.pathname()).c_str(),
327                             GetFileExInfoStandard, &data) == 0)
328   return false;
329   *size = data.nFileSizeLow;
330   return true;
331 }
332
333 bool Win32Filesystem::GetFileTime(const Pathname& path, FileTimeType which,
334                                   time_t* time) {
335   WIN32_FILE_ATTRIBUTE_DATA data = {0};
336   if (::GetFileAttributesEx(ToUtf16(path.pathname()).c_str(),
337                             GetFileExInfoStandard, &data) == 0)
338     return false;
339   switch (which) {
340   case FTT_CREATED:
341     FileTimeToUnixTime(data.ftCreationTime, time);
342     break;
343   case FTT_MODIFIED:
344     FileTimeToUnixTime(data.ftLastWriteTime, time);
345     break;
346   case FTT_ACCESSED:
347     FileTimeToUnixTime(data.ftLastAccessTime, time);
348     break;
349   default:
350     return false;
351   }
352   return true;
353 }
354
355 bool Win32Filesystem::GetAppPathname(Pathname* path) {
356   TCHAR buffer[MAX_PATH + 1];
357   if (0 == ::GetModuleFileName(NULL, buffer, ARRAY_SIZE(buffer)))
358     return false;
359   path->SetPathname(ToUtf8(buffer));
360   return true;
361 }
362
363 bool Win32Filesystem::GetAppDataFolder(Pathname* path, bool per_user) {
364   ASSERT(!organization_name_.empty());
365   ASSERT(!application_name_.empty());
366   TCHAR buffer[MAX_PATH + 1];
367   int csidl = per_user ? CSIDL_LOCAL_APPDATA : CSIDL_COMMON_APPDATA;
368   if (!::SHGetSpecialFolderPath(NULL, buffer, csidl, TRUE))
369     return false;
370   if (!IsCurrentProcessLowIntegrity() &&
371       !::GetLongPathName(buffer, buffer, ARRAY_SIZE(buffer)))
372     return false;
373   size_t len = strcatn(buffer, ARRAY_SIZE(buffer), __T("\\"));
374   len += strcpyn(buffer + len, ARRAY_SIZE(buffer) - len,
375                  ToUtf16(organization_name_).c_str());
376   if ((len > 0) && (buffer[len-1] != __T('\\'))) {
377     len += strcpyn(buffer + len, ARRAY_SIZE(buffer) - len, __T("\\"));
378   }
379   len += strcpyn(buffer + len, ARRAY_SIZE(buffer) - len,
380                  ToUtf16(application_name_).c_str());
381   if ((len > 0) && (buffer[len-1] != __T('\\'))) {
382     len += strcpyn(buffer + len, ARRAY_SIZE(buffer) - len, __T("\\"));
383   }
384   if (len >= ARRAY_SIZE(buffer) - 1)
385     return false;
386   path->clear();
387   path->SetFolder(ToUtf8(buffer));
388   return CreateFolder(*path);
389 }
390
391 bool Win32Filesystem::GetAppTempFolder(Pathname* path) {
392   if (!GetAppPathname(path))
393     return false;
394   std::string filename(path->filename());
395   return GetTemporaryFolder(*path, true, &filename);
396 }
397
398 bool Win32Filesystem::GetDiskFreeSpace(const Pathname& path, int64 *freebytes) {
399   if (!freebytes) {
400     return false;
401   }
402   char drive[4];
403   std::wstring drive16;
404   const wchar_t* target_drive = NULL;
405   if (path.GetDrive(drive, sizeof(drive))) {
406     drive16 = ToUtf16(drive);
407     target_drive = drive16.c_str();
408   } else if (path.folder().substr(0, 2) == "\\\\") {
409     // UNC path, fail.
410     // TODO: Handle UNC paths.
411     return false;
412   } else {
413     // The path is probably relative.  GetDriveType and GetDiskFreeSpaceEx
414     // use the current drive if NULL is passed as the drive name.
415     // TODO: Add method to Pathname to determine if the path is relative.
416     // TODO: Add method to Pathname to convert a path to absolute.
417   }
418   UINT driveType = ::GetDriveType(target_drive);
419   if ( (driveType & DRIVE_REMOTE) || (driveType & DRIVE_UNKNOWN) ) {
420     LOG(LS_VERBOSE) << " remove or unknown drive " << drive;
421     return false;
422   }
423
424   int64 totalNumberOfBytes;  // receives the number of bytes on disk
425   int64 totalNumberOfFreeBytes;  // receives the free bytes on disk
426   // make sure things won't change in 64 bit machine
427   // TODO replace with compile time assert
428   ASSERT(sizeof(ULARGE_INTEGER) == sizeof(uint64));  //NOLINT
429   if (::GetDiskFreeSpaceEx(target_drive,
430                            (PULARGE_INTEGER)freebytes,
431                            (PULARGE_INTEGER)&totalNumberOfBytes,
432                            (PULARGE_INTEGER)&totalNumberOfFreeBytes)) {
433     return true;
434   } else {
435     LOG(LS_VERBOSE) << " GetDiskFreeSpaceEx returns error ";
436     return false;
437   }
438 }
439
440 Pathname Win32Filesystem::GetCurrentDirectory() {
441   Pathname cwd;
442   int path_len = 0;
443   scoped_ptr<wchar_t[]> path;
444   do {
445     int needed = ::GetCurrentDirectory(path_len, path.get());
446     if (needed == 0) {
447       // Error.
448       LOG_GLE(LS_ERROR) << "::GetCurrentDirectory() failed";
449       return cwd;  // returns empty pathname
450     }
451     if (needed <= path_len) {
452       // It wrote successfully.
453       break;
454     }
455     // Else need to re-alloc for "needed".
456     path.reset(new wchar_t[needed]);
457     path_len = needed;
458   } while (true);
459   cwd.SetFolder(ToUtf8(path.get()));
460   return cwd;
461 }
462
463 // TODO: Consider overriding DeleteFolderAndContents for speed and potentially
464 // better OS integration (recycle bin?)
465 /*
466   std::wstring temp_path16 = ToUtf16(temp_path.pathname());
467   temp_path16.append(1, '*');
468   temp_path16.append(1, '\0');
469
470   SHFILEOPSTRUCT file_op = { 0 };
471   file_op.wFunc = FO_DELETE;
472   file_op.pFrom = temp_path16.c_str();
473   file_op.fFlags = FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_SILENT;
474   return (0 == SHFileOperation(&file_op));
475 */
476
477 }  // namespace talk_base