1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
|
/* -*- 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>
#ifdef MOZ_CLANG_PLUGIN
# define MOZ_RUNINIT __attribute__((annotate("moz_global_var")))
#else
# define MOZ_RUNINIT
#endif
/**
* 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.
MOZ_RUNINIT 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;
}
|