зеркало из https://github.com/mozilla/gecko-dev.git
167 строки
4.8 KiB
C++
167 строки
4.8 KiB
C++
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "updateutils_win.h"
|
|
#include <errno.h>
|
|
#include <shlwapi.h>
|
|
#include <string.h>
|
|
|
|
/**
|
|
* Note: The reason that these functions are separated from those in
|
|
* updatehelper.h/updatehelper.cpp is that those functions are strictly
|
|
* used within the updater, whereas changing functions in updateutils_win
|
|
* will have effects reaching beyond application update.
|
|
*/
|
|
|
|
// This section implements the minimum set of dirent APIs used by updater.cpp on
|
|
// Windows. If updater.cpp is modified to use more of this API, we need to
|
|
// implement those parts here too.
|
|
static dirent gDirEnt;
|
|
|
|
DIR::DIR(const WCHAR* path) : findHandle(INVALID_HANDLE_VALUE) {
|
|
memset(name, 0, sizeof(name));
|
|
wcsncpy(name, path, sizeof(name) / sizeof(name[0]));
|
|
wcsncat(name, L"\\*", sizeof(name) / sizeof(name[0]) - wcslen(name) - 1);
|
|
}
|
|
|
|
DIR::~DIR() {
|
|
if (findHandle != INVALID_HANDLE_VALUE) {
|
|
FindClose(findHandle);
|
|
}
|
|
}
|
|
|
|
dirent::dirent() { d_name[0] = L'\0'; }
|
|
|
|
DIR* opendir(const WCHAR* path) { return new DIR(path); }
|
|
|
|
int closedir(DIR* dir) {
|
|
delete dir;
|
|
return 0;
|
|
}
|
|
|
|
dirent* readdir(DIR* dir) {
|
|
WIN32_FIND_DATAW data;
|
|
if (dir->findHandle != INVALID_HANDLE_VALUE) {
|
|
BOOL result = FindNextFileW(dir->findHandle, &data);
|
|
if (!result) {
|
|
if (GetLastError() != ERROR_NO_MORE_FILES) {
|
|
errno = ENOENT;
|
|
}
|
|
return 0;
|
|
}
|
|
} else {
|
|
// Reading the first directory entry
|
|
dir->findHandle = FindFirstFileW(dir->name, &data);
|
|
if (dir->findHandle == INVALID_HANDLE_VALUE) {
|
|
if (GetLastError() == ERROR_FILE_NOT_FOUND) {
|
|
errno = ENOENT;
|
|
} else {
|
|
errno = EBADF;
|
|
}
|
|
return 0;
|
|
}
|
|
}
|
|
size_t direntBufferLength =
|
|
sizeof(gDirEnt.d_name) / sizeof(gDirEnt.d_name[0]);
|
|
wcsncpy(gDirEnt.d_name, data.cFileName, direntBufferLength);
|
|
// wcsncpy does not guarantee a null-terminated string if the source string is
|
|
// too long.
|
|
gDirEnt.d_name[direntBufferLength - 1] = '\0';
|
|
return &gDirEnt;
|
|
}
|
|
|
|
/**
|
|
* Joins a base directory path with a filename.
|
|
*
|
|
* @param base The base directory path of size MAX_PATH + 1
|
|
* @param extra The filename to append
|
|
* @return TRUE if the file name was successful appended to base
|
|
*/
|
|
BOOL PathAppendSafe(LPWSTR base, LPCWSTR extra) {
|
|
if (wcslen(base) + wcslen(extra) >= MAX_PATH) {
|
|
return FALSE;
|
|
}
|
|
|
|
return PathAppendW(base, extra);
|
|
}
|
|
|
|
/**
|
|
* Obtains a uuid as a wide string.
|
|
*
|
|
* @param outBuf
|
|
* A buffer of size MAX_PATH + 1 to store the result.
|
|
* @return TRUE if successful
|
|
*/
|
|
BOOL GetUUIDString(LPWSTR outBuf) {
|
|
UUID uuid;
|
|
RPC_WSTR uuidString = nullptr;
|
|
|
|
// Note: the return value of UuidCreate should always be RPC_S_OK on systems
|
|
// after Win2K / Win2003 due to the network hardware address no longer being
|
|
// used to create the UUID.
|
|
if (UuidCreate(&uuid) != RPC_S_OK) {
|
|
return FALSE;
|
|
}
|
|
if (UuidToStringW(&uuid, &uuidString) != RPC_S_OK) {
|
|
return FALSE;
|
|
}
|
|
if (!uuidString) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (wcslen(reinterpret_cast<LPCWSTR>(uuidString)) > MAX_PATH) {
|
|
return FALSE;
|
|
}
|
|
wcsncpy(outBuf, reinterpret_cast<LPCWSTR>(uuidString), MAX_PATH + 1);
|
|
RpcStringFreeW(&uuidString);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* Build a temporary file path whose name component is a UUID.
|
|
*
|
|
* @param basePath The base directory path for the temp file
|
|
* @param prefix Optional prefix for the beginning of the file name
|
|
* @param tmpPath Output full path, with the base directory and the file
|
|
* name. Must already have been allocated with size >= MAX_PATH.
|
|
* @return TRUE if tmpPath was successfully filled in, FALSE on errors
|
|
*/
|
|
BOOL GetUUIDTempFilePath(LPCWSTR basePath, LPCWSTR prefix, LPWSTR tmpPath) {
|
|
WCHAR filename[MAX_PATH + 1] = {L"\0"};
|
|
if (prefix) {
|
|
if (wcslen(prefix) > MAX_PATH) {
|
|
return FALSE;
|
|
}
|
|
wcsncpy(filename, prefix, MAX_PATH + 1);
|
|
}
|
|
|
|
WCHAR tmpFileNameString[MAX_PATH + 1] = {L"\0"};
|
|
if (!GetUUIDString(tmpFileNameString)) {
|
|
return FALSE;
|
|
}
|
|
|
|
size_t tmpFileNameStringLen = wcslen(tmpFileNameString);
|
|
if (wcslen(filename) + tmpFileNameStringLen > MAX_PATH) {
|
|
return FALSE;
|
|
}
|
|
wcsncat(filename, tmpFileNameString, tmpFileNameStringLen);
|
|
|
|
size_t basePathLen = wcslen(basePath);
|
|
if (basePathLen > MAX_PATH) {
|
|
return FALSE;
|
|
}
|
|
// Use basePathLen + 1 so wcsncpy will add null termination and if a caller
|
|
// doesn't allocate MAX_PATH + 1 for tmpPath this won't fail when there is
|
|
// actually enough space allocated.
|
|
wcsncpy(tmpPath, basePath, basePathLen + 1);
|
|
if (!PathAppendSafe(tmpPath, filename)) {
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|