Note: We no longer publish the latest version of our code here. We primarily use a kumc-bmi github organization. The heron ETL repository, in particular, is not public. Peers in the informatics community should see MultiSiteDev for details on requesting access.

source: webrtc/talk/base/win32filesystem.cc @ 0:4bda6873e34c

pub_scrub_3792 tip
Last change on this file since 0:4bda6873e34c was 0:4bda6873e34c, checked in by Michael Prittie <mprittie@…>, 6 years ago

Scrubbed password for publication.

File size: 16.5 KB
Line 
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
48namespace talk_base {
49
50bool 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
80FileStream *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
90bool 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
198bool 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
207bool 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
214bool 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
237std::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
247bool 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
259bool 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
281bool 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
290bool 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
298bool 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
307bool 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
313bool 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
324bool 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
333bool 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
355bool 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
363bool 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
391bool 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
398bool 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
440Pathname 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
Note: See TracBrowser for help on using the repository browser.