2020-07-03 01:32:03 +03:00
|
|
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
|
|
|
/* 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/. */
|
|
|
|
|
Backed out 12 changesets (bug 1660328, bug 1660015, bug 1649595, bug 1649596, bug 1649593, bug 1659176, bug 1659839, bug 1659838, bug 1657663, bug 1657647, bug 1655460) for xpcshell perma failures. CLOSED TREE
Backed out changeset ff95badf90e3 (bug 1660328)
Backed out changeset a92f8525ab6f (bug 1659176)
Backed out changeset 8ca05470a0d5 (bug 1659839)
Backed out changeset 5de389b735d3 (bug 1649596)
Backed out changeset 73bdddd96664 (bug 1649595)
Backed out changeset 59800d609b55 (bug 1659838)
Backed out changeset 8aca41723313 (bug 1649593)
Backed out changeset dc0d90b3e135 (bug 1657647)
Backed out changeset e3dd5b6b4fbd (bug 1657663)
Backed out changeset f9c823fa14ba (bug 1657663)
Backed out changeset a5aecc7a6469 (bug 1655460)
Backed out changeset 21b64ef30e12 (bug 1660015)
2020-08-26 06:35:20 +03:00
|
|
|
#include "mozilla/dom/IOUtils.h"
|
2020-08-10 19:00:32 +03:00
|
|
|
#include "mozilla/dom/IOUtilsBinding.h"
|
2020-07-03 01:32:03 +03:00
|
|
|
#include "mozilla/dom/Promise.h"
|
Bug 1650227: Implement IOUtils move method r=barret,Gijs
This patch introduces a move method to the IOUtils interface, which allows
for renaming/moving files or directories on disk. Source and destination
files may be specified either by an absolute path, or a relative path from
the current working directory.
This method has well-defined behaviour similar to the POSIX mv command
(except that this may create missing directories as necessary).
The behaviour is briefly summarized below:
1. If the source is a file that exists:
a. If the destination is a file that does not exist, the source is
renamed (and re-parented as a child of the destination parent
directory). The destination parent directory will be created if
necessary.
b. If the destination is a file that does exist, the destination is
replaced with the source (unless the noOverwrite option is true).
2. If the source is a directory that exists:
a. If the destination is a directory, then the source directory is
re-parented such that it becomes a child of the destination.
b. If the destination does not exist, then the source is renamed,
creating additional directories if needed.
c. If the destination is a file, then an error occurs.
3. If the source does not exist, an error occurs.
Differential Revision: https://phabricator.services.mozilla.com/D82202
2020-07-15 19:03:52 +03:00
|
|
|
#include "mozilla/ErrorNames.h"
|
|
|
|
#include "mozilla/ResultExtensions.h"
|
2020-07-15 19:04:51 +03:00
|
|
|
#include "mozilla/TextUtils.h"
|
2020-08-10 19:00:32 +03:00
|
|
|
#include "nspr/prerror.h"
|
2020-07-03 01:32:03 +03:00
|
|
|
#include "nspr/prio.h"
|
|
|
|
#include "nspr/private/pprio.h"
|
|
|
|
#include "nspr/prtypes.h"
|
2020-08-26 18:31:57 +03:00
|
|
|
#include "nspr/prtime.h"
|
Bug 1650227: Implement IOUtils move method r=barret,Gijs
This patch introduces a move method to the IOUtils interface, which allows
for renaming/moving files or directories on disk. Source and destination
files may be specified either by an absolute path, or a relative path from
the current working directory.
This method has well-defined behaviour similar to the POSIX mv command
(except that this may create missing directories as necessary).
The behaviour is briefly summarized below:
1. If the source is a file that exists:
a. If the destination is a file that does not exist, the source is
renamed (and re-parented as a child of the destination parent
directory). The destination parent directory will be created if
necessary.
b. If the destination is a file that does exist, the destination is
replaced with the source (unless the noOverwrite option is true).
2. If the source is a directory that exists:
a. If the destination is a directory, then the source directory is
re-parented such that it becomes a child of the destination.
b. If the destination does not exist, then the source is renamed,
creating additional directories if needed.
c. If the destination is a file, then an error occurs.
3. If the source does not exist, an error occurs.
Differential Revision: https://phabricator.services.mozilla.com/D82202
2020-07-15 19:03:52 +03:00
|
|
|
#include "nsIFile.h"
|
2020-07-03 01:32:03 +03:00
|
|
|
#include "nsIGlobalObject.h"
|
|
|
|
#include "nsReadableUtils.h"
|
2020-08-10 19:00:32 +03:00
|
|
|
#include "nsString.h"
|
|
|
|
#include "nsStringFwd.h"
|
2020-07-03 01:32:03 +03:00
|
|
|
#include "nsThreadManager.h"
|
|
|
|
|
|
|
|
#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
|
|
|
|
# include <fcntl.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#define REJECT_IF_NULL_EVENT_TARGET(aEventTarget, aJSPromise) \
|
|
|
|
do { \
|
|
|
|
if (!(aEventTarget)) { \
|
|
|
|
(aJSPromise) \
|
|
|
|
->MaybeRejectWithAbortError( \
|
|
|
|
"Could not dispatch task to background thread"); \
|
|
|
|
return (aJSPromise).forget(); \
|
|
|
|
} \
|
|
|
|
} while (false)
|
|
|
|
|
|
|
|
#define REJECT_IF_SHUTTING_DOWN(aJSPromise) \
|
|
|
|
do { \
|
|
|
|
if (sShutdownStarted) { \
|
|
|
|
(aJSPromise) \
|
|
|
|
->MaybeRejectWithNotAllowedError( \
|
|
|
|
"Shutting down and refusing additional I/O tasks"); \
|
|
|
|
return (aJSPromise).forget(); \
|
|
|
|
} \
|
|
|
|
} while (false)
|
|
|
|
|
2020-07-30 17:02:32 +03:00
|
|
|
#define REJECT_IF_RELATIVE_PATH(aPath, aJSPromise) \
|
|
|
|
do { \
|
|
|
|
if (!IsAbsolutePath(aPath)) { \
|
|
|
|
(aJSPromise) \
|
|
|
|
->MaybeRejectWithOperationError(nsPrintfCString( \
|
|
|
|
"Refusing to work with path(%s) because only absolute " \
|
|
|
|
"file paths are permitted", \
|
|
|
|
NS_ConvertUTF16toUTF8(aPath).get())); \
|
|
|
|
return (aJSPromise).forget(); \
|
|
|
|
} \
|
|
|
|
} while (false)
|
|
|
|
|
2020-07-03 01:32:03 +03:00
|
|
|
namespace mozilla {
|
|
|
|
namespace dom {
|
|
|
|
|
Bug 1650227: Implement IOUtils move method r=barret,Gijs
This patch introduces a move method to the IOUtils interface, which allows
for renaming/moving files or directories on disk. Source and destination
files may be specified either by an absolute path, or a relative path from
the current working directory.
This method has well-defined behaviour similar to the POSIX mv command
(except that this may create missing directories as necessary).
The behaviour is briefly summarized below:
1. If the source is a file that exists:
a. If the destination is a file that does not exist, the source is
renamed (and re-parented as a child of the destination parent
directory). The destination parent directory will be created if
necessary.
b. If the destination is a file that does exist, the destination is
replaced with the source (unless the noOverwrite option is true).
2. If the source is a directory that exists:
a. If the destination is a directory, then the source directory is
re-parented such that it becomes a child of the destination.
b. If the destination does not exist, then the source is renamed,
creating additional directories if needed.
c. If the destination is a file, then an error occurs.
3. If the source does not exist, an error occurs.
Differential Revision: https://phabricator.services.mozilla.com/D82202
2020-07-15 19:03:52 +03:00
|
|
|
// static helper functions
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Platform-specific (e.g. Windows, Unix) implementations of XPCOM APIs may
|
|
|
|
* report I/O errors inconsistently. For convenience, this function will attempt
|
|
|
|
* to match a |nsresult| against known results which imply a file cannot be
|
|
|
|
* found.
|
|
|
|
*
|
|
|
|
* @see nsLocalFileWin.cpp
|
|
|
|
* @see nsLocalFileUnix.cpp
|
|
|
|
*/
|
|
|
|
static bool IsFileNotFound(nsresult aResult) {
|
Backed out 12 changesets (bug 1660328, bug 1660015, bug 1649595, bug 1649596, bug 1649593, bug 1659176, bug 1659839, bug 1659838, bug 1657663, bug 1657647, bug 1655460) for xpcshell perma failures. CLOSED TREE
Backed out changeset ff95badf90e3 (bug 1660328)
Backed out changeset a92f8525ab6f (bug 1659176)
Backed out changeset 8ca05470a0d5 (bug 1659839)
Backed out changeset 5de389b735d3 (bug 1649596)
Backed out changeset 73bdddd96664 (bug 1649595)
Backed out changeset 59800d609b55 (bug 1659838)
Backed out changeset 8aca41723313 (bug 1649593)
Backed out changeset dc0d90b3e135 (bug 1657647)
Backed out changeset e3dd5b6b4fbd (bug 1657663)
Backed out changeset f9c823fa14ba (bug 1657663)
Backed out changeset a5aecc7a6469 (bug 1655460)
Backed out changeset 21b64ef30e12 (bug 1660015)
2020-08-26 06:35:20 +03:00
|
|
|
switch (aResult) {
|
|
|
|
case NS_ERROR_FILE_NOT_FOUND:
|
|
|
|
case NS_ERROR_FILE_TARGET_DOES_NOT_EXIST:
|
|
|
|
return true;
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
Bug 1650227: Implement IOUtils move method r=barret,Gijs
This patch introduces a move method to the IOUtils interface, which allows
for renaming/moving files or directories on disk. Source and destination
files may be specified either by an absolute path, or a relative path from
the current working directory.
This method has well-defined behaviour similar to the POSIX mv command
(except that this may create missing directories as necessary).
The behaviour is briefly summarized below:
1. If the source is a file that exists:
a. If the destination is a file that does not exist, the source is
renamed (and re-parented as a child of the destination parent
directory). The destination parent directory will be created if
necessary.
b. If the destination is a file that does exist, the destination is
replaced with the source (unless the noOverwrite option is true).
2. If the source is a directory that exists:
a. If the destination is a directory, then the source directory is
re-parented such that it becomes a child of the destination.
b. If the destination does not exist, then the source is renamed,
creating additional directories if needed.
c. If the destination is a file, then an error occurs.
3. If the source does not exist, an error occurs.
Differential Revision: https://phabricator.services.mozilla.com/D82202
2020-07-15 19:03:52 +03:00
|
|
|
}
|
|
|
|
|
2020-07-15 19:04:17 +03:00
|
|
|
/**
|
|
|
|
* Formats an error message and appends the error name to the end.
|
|
|
|
*/
|
|
|
|
template <typename... Args>
|
|
|
|
static nsCString FormatErrorMessage(nsresult aError, const char* const aMessage,
|
|
|
|
Args... aArgs) {
|
|
|
|
nsPrintfCString msg(aMessage, aArgs...);
|
|
|
|
|
|
|
|
if (const char* errName = GetStaticErrorName(aError)) {
|
|
|
|
msg.AppendPrintf(": %s", errName);
|
|
|
|
} else {
|
|
|
|
// In the exceptional case where there is no error name, print the literal
|
|
|
|
// integer value of the nsresult as an upper case hex value so it can be
|
|
|
|
// located easily in searchfox.
|
|
|
|
msg.AppendPrintf(": 0x%" PRIX32, static_cast<uint32_t>(aError));
|
|
|
|
}
|
|
|
|
|
|
|
|
return std::move(msg);
|
|
|
|
}
|
|
|
|
|
Bug 1650227: Implement IOUtils move method r=barret,Gijs
This patch introduces a move method to the IOUtils interface, which allows
for renaming/moving files or directories on disk. Source and destination
files may be specified either by an absolute path, or a relative path from
the current working directory.
This method has well-defined behaviour similar to the POSIX mv command
(except that this may create missing directories as necessary).
The behaviour is briefly summarized below:
1. If the source is a file that exists:
a. If the destination is a file that does not exist, the source is
renamed (and re-parented as a child of the destination parent
directory). The destination parent directory will be created if
necessary.
b. If the destination is a file that does exist, the destination is
replaced with the source (unless the noOverwrite option is true).
2. If the source is a directory that exists:
a. If the destination is a directory, then the source directory is
re-parented such that it becomes a child of the destination.
b. If the destination does not exist, then the source is renamed,
creating additional directories if needed.
c. If the destination is a file, then an error occurs.
3. If the source does not exist, an error occurs.
Differential Revision: https://phabricator.services.mozilla.com/D82202
2020-07-15 19:03:52 +03:00
|
|
|
static nsCString FormatErrorMessage(nsresult aError,
|
|
|
|
const char* const aMessage) {
|
|
|
|
const char* errName = GetStaticErrorName(aError);
|
|
|
|
if (errName) {
|
|
|
|
return nsPrintfCString("%s: %s", aMessage, errName);
|
|
|
|
}
|
|
|
|
// In the exceptional case where there is no error name, print the literal
|
|
|
|
// integer value of the nsresult as an upper case hex value so it can be
|
|
|
|
// located easily in searchfox.
|
|
|
|
return nsPrintfCString("%s: 0x%" PRIX32, aMessage,
|
|
|
|
static_cast<uint32_t>(aError));
|
|
|
|
}
|
|
|
|
|
2020-07-30 17:02:43 +03:00
|
|
|
/**
|
|
|
|
* Unwraps |aResult| into a |MozPromise|.
|
|
|
|
*
|
|
|
|
* If the result is an error, a new MozPromise is created and immediately
|
|
|
|
* rejected with the unwrapped error. Otherwise, if the result is ok, a new
|
|
|
|
* MozPromise is created and immediately resolved with the unwrapped result.
|
|
|
|
*/
|
|
|
|
template <class PromiseT, class OkT, class ErrT>
|
|
|
|
static RefPtr<PromiseT> ToMozPromise(Result<OkT, ErrT>& aResult,
|
|
|
|
const char* aCallSite) {
|
|
|
|
if (aResult.isErr()) {
|
|
|
|
return PromiseT::CreateAndReject(aResult.unwrapErr(), aCallSite);
|
|
|
|
}
|
|
|
|
return PromiseT::CreateAndResolve(aResult.unwrap(), aCallSite);
|
|
|
|
}
|
|
|
|
|
|
|
|
MOZ_MUST_USE inline bool ToJSValue(
|
|
|
|
JSContext* aCx, const IOUtils::InternalFileInfo& aInternalFileInfo,
|
|
|
|
JS::MutableHandle<JS::Value> aValue) {
|
|
|
|
FileInfo info;
|
|
|
|
info.mPath.Construct(aInternalFileInfo.mPath);
|
|
|
|
info.mType.Construct(aInternalFileInfo.mType);
|
|
|
|
info.mSize.Construct(aInternalFileInfo.mSize);
|
|
|
|
info.mLastModified.Construct(aInternalFileInfo.mLastModified);
|
|
|
|
return ToJSValue(aCx, info, aValue);
|
|
|
|
}
|
|
|
|
|
2020-07-15 19:04:51 +03:00
|
|
|
#ifdef XP_WIN
|
|
|
|
constexpr char PathSeparator = u'\\';
|
|
|
|
#else
|
|
|
|
constexpr char PathSeparator = u'/';
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* static */
|
|
|
|
bool IOUtils::IsAbsolutePath(const nsAString& aPath) {
|
|
|
|
// NB: This impl is adapted from js::shell::IsAbsolutePath(JSLinearString*).
|
|
|
|
const size_t length = aPath.Length();
|
|
|
|
|
|
|
|
#ifdef XP_WIN
|
|
|
|
// On Windows there are various forms of absolute paths (see
|
|
|
|
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247%28v=vs.85%29.aspx
|
|
|
|
// for details):
|
|
|
|
//
|
|
|
|
// "\..."
|
|
|
|
// "\\..."
|
|
|
|
// "C:\..."
|
|
|
|
//
|
|
|
|
// The first two cases are handled by the common test below so we only need a
|
|
|
|
// specific test for the last one here.
|
|
|
|
|
|
|
|
if (length > 3 && mozilla::IsAsciiAlpha(aPath.CharAt(0)) &&
|
|
|
|
aPath.CharAt(1) == u':' && aPath.CharAt(2) == u'\\') {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
return length > 0 && aPath.CharAt(0) == PathSeparator;
|
|
|
|
}
|
|
|
|
|
Bug 1650227: Implement IOUtils move method r=barret,Gijs
This patch introduces a move method to the IOUtils interface, which allows
for renaming/moving files or directories on disk. Source and destination
files may be specified either by an absolute path, or a relative path from
the current working directory.
This method has well-defined behaviour similar to the POSIX mv command
(except that this may create missing directories as necessary).
The behaviour is briefly summarized below:
1. If the source is a file that exists:
a. If the destination is a file that does not exist, the source is
renamed (and re-parented as a child of the destination parent
directory). The destination parent directory will be created if
necessary.
b. If the destination is a file that does exist, the destination is
replaced with the source (unless the noOverwrite option is true).
2. If the source is a directory that exists:
a. If the destination is a directory, then the source directory is
re-parented such that it becomes a child of the destination.
b. If the destination does not exist, then the source is renamed,
creating additional directories if needed.
c. If the destination is a file, then an error occurs.
3. If the source does not exist, an error occurs.
Differential Revision: https://phabricator.services.mozilla.com/D82202
2020-07-15 19:03:52 +03:00
|
|
|
// IOUtils implementation
|
|
|
|
|
2020-07-03 01:32:03 +03:00
|
|
|
/* static */
|
|
|
|
StaticDataMutex<StaticRefPtr<nsISerialEventTarget>>
|
|
|
|
IOUtils::sBackgroundEventTarget("sBackgroundEventTarget");
|
|
|
|
/* static */
|
|
|
|
StaticRefPtr<nsIAsyncShutdownClient> IOUtils::sBarrier;
|
|
|
|
/* static */
|
|
|
|
Atomic<bool> IOUtils::sShutdownStarted = Atomic<bool>(false);
|
|
|
|
|
2020-07-30 17:03:03 +03:00
|
|
|
template <typename MozPromiseT, typename Fn, typename... Args>
|
|
|
|
static RefPtr<MozPromiseT> InvokeToMozPromise(Fn aFunc, Args... aArgs) {
|
|
|
|
MOZ_ASSERT(!NS_IsMainThread());
|
|
|
|
auto rv = aFunc(std::forward<Args>(aArgs)...);
|
|
|
|
if (rv.isErr()) {
|
|
|
|
return MozPromiseT::CreateAndReject(rv.unwrapErr(), __func__);
|
|
|
|
}
|
|
|
|
return MozPromiseT::CreateAndResolve(rv.unwrap(), __func__);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* static */
|
|
|
|
template <typename OkT, typename Fn, typename... Args>
|
|
|
|
already_AddRefed<Promise> IOUtils::RunOnBackgroundThread(
|
|
|
|
RefPtr<Promise>& aPromise, Fn aFunc, Args... aArgs) {
|
|
|
|
RefPtr<nsISerialEventTarget> bg = GetBackgroundEventTarget();
|
|
|
|
REJECT_IF_NULL_EVENT_TARGET(bg, aPromise);
|
|
|
|
|
|
|
|
InvokeAsync(
|
|
|
|
bg, __func__,
|
|
|
|
[fn = aFunc, argsTuple = std::make_tuple(std::move(aArgs)...)]() mutable {
|
|
|
|
return std::apply(
|
|
|
|
[fn](Args... args) mutable {
|
|
|
|
using MozPromiseT = MozPromise<OkT, IOError, true>;
|
|
|
|
return InvokeToMozPromise<MozPromiseT>(
|
|
|
|
fn, std::forward<Args>(args)...);
|
|
|
|
},
|
|
|
|
std::move(argsTuple));
|
|
|
|
})
|
|
|
|
->Then(
|
|
|
|
GetCurrentSerialEventTarget(), __func__,
|
|
|
|
[promise = RefPtr(aPromise)](const OkT& ok) {
|
|
|
|
if constexpr (std::is_same_v<OkT, nsTArray<uint8_t>>) {
|
|
|
|
TypedArrayCreator<Uint8Array> arr(ok);
|
|
|
|
promise->MaybeResolve(arr);
|
|
|
|
} else if constexpr (std::is_same_v<OkT, Ok>) {
|
|
|
|
promise->MaybeResolveWithUndefined();
|
|
|
|
} else {
|
|
|
|
promise->MaybeResolve(ok);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
[promise = RefPtr(aPromise)](const IOError& err) {
|
|
|
|
RejectJSPromise(promise, err);
|
|
|
|
});
|
|
|
|
return aPromise.forget();
|
|
|
|
}
|
|
|
|
|
2020-07-03 01:32:03 +03:00
|
|
|
/* static */
|
|
|
|
already_AddRefed<Promise> IOUtils::Read(GlobalObject& aGlobal,
|
|
|
|
const nsAString& aPath,
|
|
|
|
const Optional<uint32_t>& aMaxBytes) {
|
|
|
|
RefPtr<Promise> promise = CreateJSPromise(aGlobal);
|
|
|
|
NS_ENSURE_TRUE(!!promise, nullptr);
|
2020-08-10 20:51:13 +03:00
|
|
|
REJECT_IF_SHUTTING_DOWN(promise);
|
2020-07-30 17:02:32 +03:00
|
|
|
|
2020-07-03 01:32:03 +03:00
|
|
|
// Process arguments.
|
2020-07-30 17:03:03 +03:00
|
|
|
REJECT_IF_RELATIVE_PATH(aPath, promise);
|
|
|
|
nsAutoString path(aPath);
|
2020-07-30 17:02:34 +03:00
|
|
|
Maybe<uint32_t> toRead = Nothing();
|
2020-07-03 01:32:03 +03:00
|
|
|
if (aMaxBytes.WasPassed()) {
|
2020-07-30 17:02:34 +03:00
|
|
|
if (aMaxBytes.Value() == 0) {
|
2020-07-03 01:32:03 +03:00
|
|
|
// Resolve with an empty buffer.
|
|
|
|
nsTArray<uint8_t> arr(0);
|
2020-07-30 17:03:03 +03:00
|
|
|
promise->MaybeResolve(TypedArrayCreator<Uint8Array>(arr));
|
2020-07-03 01:32:03 +03:00
|
|
|
return promise.forget();
|
|
|
|
}
|
2020-07-30 17:02:34 +03:00
|
|
|
toRead.emplace(aMaxBytes.Value());
|
2020-07-03 01:32:03 +03:00
|
|
|
}
|
|
|
|
|
2020-07-30 17:03:03 +03:00
|
|
|
return RunOnBackgroundThread<nsTArray<uint8_t>>(promise, &ReadSync, path,
|
|
|
|
toRead);
|
2020-07-03 01:32:03 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/* static */
|
|
|
|
already_AddRefed<Promise> IOUtils::WriteAtomic(
|
|
|
|
GlobalObject& aGlobal, const nsAString& aPath, const Uint8Array& aData,
|
|
|
|
const WriteAtomicOptions& aOptions) {
|
|
|
|
RefPtr<Promise> promise = CreateJSPromise(aGlobal);
|
|
|
|
NS_ENSURE_TRUE(!!promise, nullptr);
|
2020-08-10 20:51:13 +03:00
|
|
|
REJECT_IF_SHUTTING_DOWN(promise);
|
2020-07-30 17:02:32 +03:00
|
|
|
|
2020-07-03 01:32:03 +03:00
|
|
|
// Process arguments.
|
2020-08-10 19:00:32 +03:00
|
|
|
REJECT_IF_RELATIVE_PATH(aPath, promise);
|
2020-07-03 01:32:03 +03:00
|
|
|
aData.ComputeState();
|
2020-08-07 10:49:47 +03:00
|
|
|
auto buf = Buffer<uint8_t>::CopyFrom(Span(aData.Data(), aData.Length()));
|
2020-07-30 17:03:03 +03:00
|
|
|
if (buf.isNothing()) {
|
Backed out 12 changesets (bug 1660328, bug 1660015, bug 1649595, bug 1649596, bug 1649593, bug 1659176, bug 1659839, bug 1659838, bug 1657663, bug 1657647, bug 1655460) for xpcshell perma failures. CLOSED TREE
Backed out changeset ff95badf90e3 (bug 1660328)
Backed out changeset a92f8525ab6f (bug 1659176)
Backed out changeset 8ca05470a0d5 (bug 1659839)
Backed out changeset 5de389b735d3 (bug 1649596)
Backed out changeset 73bdddd96664 (bug 1649595)
Backed out changeset 59800d609b55 (bug 1659838)
Backed out changeset 8aca41723313 (bug 1649593)
Backed out changeset dc0d90b3e135 (bug 1657647)
Backed out changeset e3dd5b6b4fbd (bug 1657663)
Backed out changeset f9c823fa14ba (bug 1657663)
Backed out changeset a5aecc7a6469 (bug 1655460)
Backed out changeset 21b64ef30e12 (bug 1660015)
2020-08-26 06:35:20 +03:00
|
|
|
promise->MaybeRejectWithOperationError("Out of memory");
|
2020-07-03 01:32:03 +03:00
|
|
|
return promise.forget();
|
|
|
|
}
|
2020-07-30 17:03:03 +03:00
|
|
|
nsAutoString destPath(aPath);
|
Backed out 12 changesets (bug 1660328, bug 1660015, bug 1649595, bug 1649596, bug 1649593, bug 1659176, bug 1659839, bug 1659838, bug 1657663, bug 1657647, bug 1655460) for xpcshell perma failures. CLOSED TREE
Backed out changeset ff95badf90e3 (bug 1660328)
Backed out changeset a92f8525ab6f (bug 1659176)
Backed out changeset 8ca05470a0d5 (bug 1659839)
Backed out changeset 5de389b735d3 (bug 1649596)
Backed out changeset 73bdddd96664 (bug 1649595)
Backed out changeset 59800d609b55 (bug 1659838)
Backed out changeset 8aca41723313 (bug 1649593)
Backed out changeset dc0d90b3e135 (bug 1657647)
Backed out changeset e3dd5b6b4fbd (bug 1657663)
Backed out changeset f9c823fa14ba (bug 1657663)
Backed out changeset a5aecc7a6469 (bug 1655460)
Backed out changeset 21b64ef30e12 (bug 1660015)
2020-08-26 06:35:20 +03:00
|
|
|
InternalWriteAtomicOpts opts;
|
|
|
|
opts.mFlush = aOptions.mFlush;
|
|
|
|
opts.mNoOverwrite = aOptions.mNoOverwrite;
|
|
|
|
if (aOptions.mBackupFile.WasPassed()) {
|
|
|
|
opts.mBackupFile.emplace(aOptions.mBackupFile.Value());
|
|
|
|
}
|
|
|
|
if (aOptions.mTmpPath.WasPassed()) {
|
|
|
|
opts.mTmpPath.emplace(aOptions.mTmpPath.Value());
|
|
|
|
}
|
2020-07-03 01:32:03 +03:00
|
|
|
|
2020-07-30 17:03:03 +03:00
|
|
|
return RunOnBackgroundThread<uint32_t>(promise, &WriteAtomicSync, destPath,
|
|
|
|
std::move(*buf), std::move(opts));
|
2020-07-03 01:32:03 +03:00
|
|
|
}
|
|
|
|
|
Bug 1650227: Implement IOUtils move method r=barret,Gijs
This patch introduces a move method to the IOUtils interface, which allows
for renaming/moving files or directories on disk. Source and destination
files may be specified either by an absolute path, or a relative path from
the current working directory.
This method has well-defined behaviour similar to the POSIX mv command
(except that this may create missing directories as necessary).
The behaviour is briefly summarized below:
1. If the source is a file that exists:
a. If the destination is a file that does not exist, the source is
renamed (and re-parented as a child of the destination parent
directory). The destination parent directory will be created if
necessary.
b. If the destination is a file that does exist, the destination is
replaced with the source (unless the noOverwrite option is true).
2. If the source is a directory that exists:
a. If the destination is a directory, then the source directory is
re-parented such that it becomes a child of the destination.
b. If the destination does not exist, then the source is renamed,
creating additional directories if needed.
c. If the destination is a file, then an error occurs.
3. If the source does not exist, an error occurs.
Differential Revision: https://phabricator.services.mozilla.com/D82202
2020-07-15 19:03:52 +03:00
|
|
|
/* static */
|
|
|
|
already_AddRefed<Promise> IOUtils::Move(GlobalObject& aGlobal,
|
|
|
|
const nsAString& aSourcePath,
|
|
|
|
const nsAString& aDestPath,
|
|
|
|
const MoveOptions& aOptions) {
|
|
|
|
RefPtr<Promise> promise = CreateJSPromise(aGlobal);
|
|
|
|
NS_ENSURE_TRUE(!!promise, nullptr);
|
2020-08-10 20:51:13 +03:00
|
|
|
REJECT_IF_SHUTTING_DOWN(promise);
|
2020-07-30 17:02:32 +03:00
|
|
|
|
2020-07-30 17:03:03 +03:00
|
|
|
// Process arguments.
|
2020-07-30 17:02:32 +03:00
|
|
|
REJECT_IF_RELATIVE_PATH(aSourcePath, promise);
|
|
|
|
REJECT_IF_RELATIVE_PATH(aDestPath, promise);
|
2020-07-30 17:03:03 +03:00
|
|
|
nsAutoString sourcePath(aSourcePath);
|
|
|
|
nsAutoString destPath(aDestPath);
|
Bug 1650227: Implement IOUtils move method r=barret,Gijs
This patch introduces a move method to the IOUtils interface, which allows
for renaming/moving files or directories on disk. Source and destination
files may be specified either by an absolute path, or a relative path from
the current working directory.
This method has well-defined behaviour similar to the POSIX mv command
(except that this may create missing directories as necessary).
The behaviour is briefly summarized below:
1. If the source is a file that exists:
a. If the destination is a file that does not exist, the source is
renamed (and re-parented as a child of the destination parent
directory). The destination parent directory will be created if
necessary.
b. If the destination is a file that does exist, the destination is
replaced with the source (unless the noOverwrite option is true).
2. If the source is a directory that exists:
a. If the destination is a directory, then the source directory is
re-parented such that it becomes a child of the destination.
b. If the destination does not exist, then the source is renamed,
creating additional directories if needed.
c. If the destination is a file, then an error occurs.
3. If the source does not exist, an error occurs.
Differential Revision: https://phabricator.services.mozilla.com/D82202
2020-07-15 19:03:52 +03:00
|
|
|
|
2020-07-30 17:03:03 +03:00
|
|
|
return RunOnBackgroundThread<Ok>(promise, &MoveSync, sourcePath, destPath,
|
2020-08-10 19:00:32 +03:00
|
|
|
aOptions.mNoOverwrite);
|
Bug 1650227: Implement IOUtils move method r=barret,Gijs
This patch introduces a move method to the IOUtils interface, which allows
for renaming/moving files or directories on disk. Source and destination
files may be specified either by an absolute path, or a relative path from
the current working directory.
This method has well-defined behaviour similar to the POSIX mv command
(except that this may create missing directories as necessary).
The behaviour is briefly summarized below:
1. If the source is a file that exists:
a. If the destination is a file that does not exist, the source is
renamed (and re-parented as a child of the destination parent
directory). The destination parent directory will be created if
necessary.
b. If the destination is a file that does exist, the destination is
replaced with the source (unless the noOverwrite option is true).
2. If the source is a directory that exists:
a. If the destination is a directory, then the source directory is
re-parented such that it becomes a child of the destination.
b. If the destination does not exist, then the source is renamed,
creating additional directories if needed.
c. If the destination is a file, then an error occurs.
3. If the source does not exist, an error occurs.
Differential Revision: https://phabricator.services.mozilla.com/D82202
2020-07-15 19:03:52 +03:00
|
|
|
}
|
|
|
|
|
2020-07-18 03:31:57 +03:00
|
|
|
/* static */
|
|
|
|
already_AddRefed<Promise> IOUtils::Remove(GlobalObject& aGlobal,
|
|
|
|
const nsAString& aPath,
|
|
|
|
const RemoveOptions& aOptions) {
|
|
|
|
RefPtr<Promise> promise = CreateJSPromise(aGlobal);
|
2020-07-30 17:02:32 +03:00
|
|
|
NS_ENSURE_TRUE(!!promise, nullptr);
|
2020-08-10 20:51:13 +03:00
|
|
|
REJECT_IF_SHUTTING_DOWN(promise);
|
2020-07-30 17:02:32 +03:00
|
|
|
|
|
|
|
REJECT_IF_RELATIVE_PATH(aPath, promise);
|
2020-07-30 17:03:03 +03:00
|
|
|
nsAutoString path(aPath);
|
2020-07-30 16:11:00 +03:00
|
|
|
|
2020-07-30 17:03:03 +03:00
|
|
|
return RunOnBackgroundThread<Ok>(promise, &RemoveSync, path,
|
|
|
|
aOptions.mIgnoreAbsent, aOptions.mRecursive);
|
2020-07-18 03:31:57 +03:00
|
|
|
}
|
|
|
|
|
2020-07-21 18:13:35 +03:00
|
|
|
/* static */
|
|
|
|
already_AddRefed<Promise> IOUtils::MakeDirectory(
|
|
|
|
GlobalObject& aGlobal, const nsAString& aPath,
|
|
|
|
const MakeDirectoryOptions& aOptions) {
|
|
|
|
RefPtr<Promise> promise = CreateJSPromise(aGlobal);
|
2020-07-30 17:02:32 +03:00
|
|
|
NS_ENSURE_TRUE(!!promise, nullptr);
|
2020-08-10 20:51:13 +03:00
|
|
|
REJECT_IF_SHUTTING_DOWN(promise);
|
2020-07-30 17:02:32 +03:00
|
|
|
|
|
|
|
REJECT_IF_RELATIVE_PATH(aPath, promise);
|
2020-07-30 17:03:03 +03:00
|
|
|
nsAutoString path(aPath);
|
2020-07-30 00:51:33 +03:00
|
|
|
|
2020-07-30 17:03:03 +03:00
|
|
|
return RunOnBackgroundThread<Ok>(promise, &CreateDirectorySync, path,
|
|
|
|
aOptions.mCreateAncestors,
|
|
|
|
aOptions.mIgnoreExisting, 0777);
|
2020-07-23 21:15:30 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
already_AddRefed<Promise> IOUtils::Stat(GlobalObject& aGlobal,
|
|
|
|
const nsAString& aPath) {
|
|
|
|
RefPtr<Promise> promise = CreateJSPromise(aGlobal);
|
2020-07-30 17:02:32 +03:00
|
|
|
NS_ENSURE_TRUE(!!promise, nullptr);
|
2020-08-10 20:51:13 +03:00
|
|
|
REJECT_IF_SHUTTING_DOWN(promise);
|
2020-07-30 17:02:32 +03:00
|
|
|
|
|
|
|
REJECT_IF_RELATIVE_PATH(aPath, promise);
|
2020-07-30 17:03:03 +03:00
|
|
|
nsAutoString path(aPath);
|
2020-07-30 16:11:00 +03:00
|
|
|
|
2020-07-30 17:03:03 +03:00
|
|
|
return RunOnBackgroundThread<InternalFileInfo>(promise, &StatSync, path);
|
2020-07-21 18:13:35 +03:00
|
|
|
}
|
|
|
|
|
2020-08-10 19:00:32 +03:00
|
|
|
/* static */
|
|
|
|
already_AddRefed<Promise> IOUtils::Copy(GlobalObject& aGlobal,
|
|
|
|
const nsAString& aSourcePath,
|
|
|
|
const nsAString& aDestPath,
|
|
|
|
const CopyOptions& aOptions) {
|
|
|
|
RefPtr<Promise> promise = CreateJSPromise(aGlobal);
|
|
|
|
NS_ENSURE_TRUE(!!promise, nullptr);
|
2020-08-10 20:51:13 +03:00
|
|
|
REJECT_IF_SHUTTING_DOWN(promise);
|
2020-08-10 19:00:32 +03:00
|
|
|
|
|
|
|
// Process arguments.
|
|
|
|
REJECT_IF_RELATIVE_PATH(aSourcePath, promise);
|
|
|
|
REJECT_IF_RELATIVE_PATH(aDestPath, promise);
|
|
|
|
nsAutoString sourcePath(aSourcePath);
|
|
|
|
nsAutoString destPath(aDestPath);
|
|
|
|
|
|
|
|
return RunOnBackgroundThread<Ok>(promise, &CopySync, sourcePath, destPath,
|
|
|
|
aOptions.mNoOverwrite, aOptions.mRecursive);
|
|
|
|
}
|
|
|
|
|
2020-08-26 18:31:57 +03:00
|
|
|
/* static */
|
|
|
|
already_AddRefed<Promise> IOUtils::Touch(
|
|
|
|
GlobalObject& aGlobal, const nsAString& aPath,
|
|
|
|
const Optional<int64_t>& aModification) {
|
|
|
|
RefPtr<Promise> promise = CreateJSPromise(aGlobal);
|
|
|
|
NS_ENSURE_TRUE(!!promise, nullptr);
|
|
|
|
|
|
|
|
REJECT_IF_RELATIVE_PATH(aPath, promise);
|
|
|
|
nsAutoString path(aPath);
|
|
|
|
|
|
|
|
Maybe<int64_t> newTime = Nothing();
|
|
|
|
if (aModification.WasPassed()) {
|
|
|
|
newTime = Some(aModification.Value());
|
|
|
|
}
|
|
|
|
|
|
|
|
return RunOnBackgroundThread<int64_t>(promise, &TouchSync, path, newTime);
|
|
|
|
}
|
|
|
|
|
2020-07-03 01:32:03 +03:00
|
|
|
/* static */
|
|
|
|
already_AddRefed<nsISerialEventTarget> IOUtils::GetBackgroundEventTarget() {
|
|
|
|
if (sShutdownStarted) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto lockedBackgroundEventTarget = sBackgroundEventTarget.Lock();
|
|
|
|
if (!lockedBackgroundEventTarget.ref()) {
|
|
|
|
RefPtr<nsISerialEventTarget> et;
|
|
|
|
MOZ_ALWAYS_SUCCEEDS(NS_CreateBackgroundTaskQueue(
|
|
|
|
"IOUtils::BackgroundIOThread", getter_AddRefs(et)));
|
|
|
|
MOZ_ASSERT(et);
|
|
|
|
*lockedBackgroundEventTarget = et;
|
|
|
|
|
|
|
|
if (NS_IsMainThread()) {
|
|
|
|
IOUtils::SetShutdownHooks();
|
|
|
|
} else {
|
|
|
|
nsCOMPtr<nsIRunnable> runnable = NS_NewRunnableFunction(
|
|
|
|
__func__, []() { IOUtils::SetShutdownHooks(); });
|
|
|
|
NS_DispatchToMainThread(runnable.forget());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return do_AddRef(*lockedBackgroundEventTarget);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* static */
|
|
|
|
already_AddRefed<nsIAsyncShutdownClient> IOUtils::GetShutdownBarrier() {
|
|
|
|
MOZ_RELEASE_ASSERT(NS_IsMainThread());
|
|
|
|
|
|
|
|
if (!sBarrier) {
|
|
|
|
nsCOMPtr<nsIAsyncShutdownService> svc = services::GetAsyncShutdownService();
|
|
|
|
MOZ_ASSERT(svc);
|
|
|
|
|
|
|
|
nsCOMPtr<nsIAsyncShutdownClient> barrier;
|
Backed out 12 changesets (bug 1660328, bug 1660015, bug 1649595, bug 1649596, bug 1649593, bug 1659176, bug 1659839, bug 1659838, bug 1657663, bug 1657647, bug 1655460) for xpcshell perma failures. CLOSED TREE
Backed out changeset ff95badf90e3 (bug 1660328)
Backed out changeset a92f8525ab6f (bug 1659176)
Backed out changeset 8ca05470a0d5 (bug 1659839)
Backed out changeset 5de389b735d3 (bug 1649596)
Backed out changeset 73bdddd96664 (bug 1649595)
Backed out changeset 59800d609b55 (bug 1659838)
Backed out changeset 8aca41723313 (bug 1649593)
Backed out changeset dc0d90b3e135 (bug 1657647)
Backed out changeset e3dd5b6b4fbd (bug 1657663)
Backed out changeset f9c823fa14ba (bug 1657663)
Backed out changeset a5aecc7a6469 (bug 1655460)
Backed out changeset 21b64ef30e12 (bug 1660015)
2020-08-26 06:35:20 +03:00
|
|
|
nsresult rv = svc->GetXpcomWillShutdown(getter_AddRefs(barrier));
|
2020-07-03 01:32:03 +03:00
|
|
|
NS_ENSURE_SUCCESS(rv, nullptr);
|
|
|
|
sBarrier = barrier;
|
|
|
|
}
|
|
|
|
return do_AddRef(sBarrier);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* static */
|
|
|
|
void IOUtils::SetShutdownHooks() {
|
|
|
|
MOZ_RELEASE_ASSERT(NS_IsMainThread());
|
|
|
|
|
|
|
|
nsCOMPtr<nsIAsyncShutdownClient> barrier = GetShutdownBarrier();
|
|
|
|
nsCOMPtr<nsIAsyncShutdownBlocker> blocker = new IOUtilsShutdownBlocker();
|
|
|
|
|
|
|
|
nsresult rv = barrier->AddBlocker(
|
|
|
|
blocker, NS_LITERAL_STRING_FROM_CSTRING(__FILE__), __LINE__,
|
|
|
|
u"IOUtils: waiting for pending I/O to finish"_ns);
|
|
|
|
// Adding a new shutdown blocker should only fail if the current shutdown
|
|
|
|
// phase has completed. Ensure that we have set our shutdown flag to stop
|
|
|
|
// accepting new I/O tasks in this case.
|
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
sShutdownStarted = true;
|
|
|
|
}
|
|
|
|
NS_ENSURE_SUCCESS_VOID(rv);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* static */
|
|
|
|
already_AddRefed<Promise> IOUtils::CreateJSPromise(GlobalObject& aGlobal) {
|
|
|
|
ErrorResult er;
|
|
|
|
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
|
|
|
|
RefPtr<Promise> promise = Promise::Create(global, er);
|
|
|
|
if (er.Failed()) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
MOZ_ASSERT(promise);
|
|
|
|
return do_AddRef(promise);
|
|
|
|
}
|
|
|
|
|
2020-07-30 17:02:50 +03:00
|
|
|
/* static */
|
|
|
|
void IOUtils::RejectJSPromise(const RefPtr<Promise>& aPromise,
|
|
|
|
const IOError& aError) {
|
|
|
|
const auto& errMsg = aError.Message();
|
|
|
|
|
|
|
|
switch (aError.Code()) {
|
|
|
|
case NS_ERROR_FILE_TARGET_DOES_NOT_EXIST:
|
|
|
|
case NS_ERROR_FILE_NOT_FOUND:
|
|
|
|
aPromise->MaybeRejectWithNotFoundError(errMsg.refOr("File not found"_ns));
|
|
|
|
break;
|
|
|
|
case NS_ERROR_FILE_ACCESS_DENIED:
|
|
|
|
aPromise->MaybeRejectWithNotAllowedError(
|
|
|
|
errMsg.refOr("Access was denied to the target file"_ns));
|
|
|
|
break;
|
|
|
|
case NS_ERROR_FILE_TOO_BIG:
|
|
|
|
aPromise->MaybeRejectWithNotReadableError(
|
|
|
|
errMsg.refOr("Target file is too big"_ns));
|
|
|
|
break;
|
|
|
|
case NS_ERROR_FILE_ALREADY_EXISTS:
|
|
|
|
aPromise->MaybeRejectWithNoModificationAllowedError(
|
|
|
|
errMsg.refOr("Target file already exists"_ns));
|
|
|
|
break;
|
|
|
|
case NS_ERROR_FILE_COPY_OR_MOVE_FAILED:
|
|
|
|
aPromise->MaybeRejectWithOperationError(
|
|
|
|
errMsg.refOr("Failed to copy or move the target file"_ns));
|
|
|
|
break;
|
|
|
|
case NS_ERROR_FILE_READ_ONLY:
|
|
|
|
aPromise->MaybeRejectWithReadOnlyError(
|
|
|
|
errMsg.refOr("Target file is read only"_ns));
|
|
|
|
break;
|
|
|
|
case NS_ERROR_FILE_NOT_DIRECTORY:
|
|
|
|
case NS_ERROR_FILE_DESTINATION_NOT_DIR:
|
|
|
|
aPromise->MaybeRejectWithInvalidAccessError(
|
|
|
|
errMsg.refOr("Target file is not a directory"_ns));
|
|
|
|
break;
|
|
|
|
case NS_ERROR_FILE_UNRECOGNIZED_PATH:
|
|
|
|
aPromise->MaybeRejectWithOperationError(
|
|
|
|
errMsg.refOr("Target file path is not recognized"_ns));
|
|
|
|
break;
|
|
|
|
case NS_ERROR_FILE_DIR_NOT_EMPTY:
|
|
|
|
aPromise->MaybeRejectWithOperationError(
|
|
|
|
errMsg.refOr("Target directory is not empty"_ns));
|
|
|
|
break;
|
2020-08-26 18:31:57 +03:00
|
|
|
case NS_ERROR_ILLEGAL_INPUT:
|
|
|
|
case NS_ERROR_ILLEGAL_VALUE:
|
|
|
|
aPromise->MaybeRejectWithDataError(
|
|
|
|
errMsg.refOr("Argument is not allowed"_ns));
|
|
|
|
break;
|
2020-07-30 17:02:50 +03:00
|
|
|
default:
|
|
|
|
aPromise->MaybeRejectWithUnknownError(
|
|
|
|
errMsg.refOr(FormatErrorMessage(aError.Code(), "Unexpected error")));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-03 01:32:03 +03:00
|
|
|
/* static */
|
|
|
|
UniquePtr<PRFileDesc, PR_CloseDelete> IOUtils::OpenExistingSync(
|
2020-07-15 19:04:17 +03:00
|
|
|
const nsAString& aPath, int32_t aFlags) {
|
2020-07-30 17:02:43 +03:00
|
|
|
MOZ_ASSERT(!NS_IsMainThread());
|
2020-07-03 01:32:03 +03:00
|
|
|
// Ensure that CREATE_FILE and EXCL flags were not included, as we do not
|
|
|
|
// want to create a new file.
|
|
|
|
MOZ_ASSERT((aFlags & (PR_CREATE_FILE | PR_EXCL)) == 0);
|
Backed out 12 changesets (bug 1660328, bug 1660015, bug 1649595, bug 1649596, bug 1649593, bug 1659176, bug 1659839, bug 1659838, bug 1657663, bug 1657647, bug 1655460) for xpcshell perma failures. CLOSED TREE
Backed out changeset ff95badf90e3 (bug 1660328)
Backed out changeset a92f8525ab6f (bug 1659176)
Backed out changeset 8ca05470a0d5 (bug 1659839)
Backed out changeset 5de389b735d3 (bug 1649596)
Backed out changeset 73bdddd96664 (bug 1649595)
Backed out changeset 59800d609b55 (bug 1659838)
Backed out changeset 8aca41723313 (bug 1649593)
Backed out changeset dc0d90b3e135 (bug 1657647)
Backed out changeset e3dd5b6b4fbd (bug 1657663)
Backed out changeset f9c823fa14ba (bug 1657663)
Backed out changeset a5aecc7a6469 (bug 1655460)
Backed out changeset 21b64ef30e12 (bug 1660015)
2020-08-26 06:35:20 +03:00
|
|
|
|
2020-07-15 19:04:17 +03:00
|
|
|
// We open the file descriptor through an nsLocalFile to ensure that the paths
|
|
|
|
// are interpreted/encoded correctly on all platforms.
|
|
|
|
RefPtr<nsLocalFile> file = new nsLocalFile();
|
|
|
|
nsresult rv = file->InitWithPath(aPath);
|
|
|
|
NS_ENSURE_SUCCESS(rv, nullptr);
|
|
|
|
|
|
|
|
PRFileDesc* fd;
|
|
|
|
rv = file->OpenNSPRFileDesc(aFlags, /* mode */ 0, &fd);
|
|
|
|
NS_ENSURE_SUCCESS(rv, nullptr);
|
|
|
|
|
|
|
|
return UniquePtr<PRFileDesc, PR_CloseDelete>(fd);
|
2020-07-03 01:32:03 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/* static */
|
2020-07-15 19:04:17 +03:00
|
|
|
UniquePtr<PRFileDesc, PR_CloseDelete> IOUtils::CreateFileSync(
|
|
|
|
const nsAString& aPath, int32_t aFlags, int32_t aMode) {
|
2020-07-30 17:02:43 +03:00
|
|
|
MOZ_ASSERT(!NS_IsMainThread());
|
2020-07-15 19:04:17 +03:00
|
|
|
// We open the file descriptor through an nsLocalFile to ensure that the paths
|
|
|
|
// are interpreted/encoded correctly on all platforms.
|
|
|
|
RefPtr<nsLocalFile> file = new nsLocalFile();
|
|
|
|
nsresult rv = file->InitWithPath(aPath);
|
|
|
|
NS_ENSURE_SUCCESS(rv, nullptr);
|
|
|
|
|
|
|
|
PRFileDesc* fd;
|
|
|
|
rv = file->OpenNSPRFileDesc(aFlags | PR_CREATE_FILE | PR_EXCL, aMode, &fd);
|
|
|
|
NS_ENSURE_SUCCESS(rv, nullptr);
|
|
|
|
|
|
|
|
return UniquePtr<PRFileDesc, PR_CloseDelete>(fd);
|
2020-07-03 01:32:03 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/* static */
|
2020-07-30 17:02:50 +03:00
|
|
|
Result<nsTArray<uint8_t>, IOUtils::IOError> IOUtils::ReadSync(
|
2020-07-30 17:02:34 +03:00
|
|
|
const nsAString& aPath, const Maybe<uint32_t>& aMaxBytes) {
|
2020-07-03 01:32:03 +03:00
|
|
|
MOZ_ASSERT(!NS_IsMainThread());
|
|
|
|
|
2020-07-30 17:02:34 +03:00
|
|
|
UniquePtr<PRFileDesc, PR_CloseDelete> fd = OpenExistingSync(aPath, PR_RDONLY);
|
|
|
|
if (!fd) {
|
2020-07-30 17:02:50 +03:00
|
|
|
return Err(IOError(NS_ERROR_FILE_NOT_FOUND)
|
|
|
|
.WithMessage("Could not open the file at %s",
|
|
|
|
NS_ConvertUTF16toUTF8(aPath).get()));
|
2020-07-30 17:02:34 +03:00
|
|
|
}
|
|
|
|
uint32_t bufSize;
|
|
|
|
if (aMaxBytes.isNothing()) {
|
|
|
|
// Limitation: We cannot read files that are larger than the max size of a
|
2020-07-30 17:02:50 +03:00
|
|
|
// TypedArray (UINT32_MAX bytes). Reject if the file is too
|
|
|
|
// big to be read.
|
2020-07-30 17:02:34 +03:00
|
|
|
PRFileInfo64 info;
|
|
|
|
if (PR_FAILURE == PR_GetOpenFileInfo64(fd.get(), &info)) {
|
2020-07-30 17:02:50 +03:00
|
|
|
return Err(IOError(NS_ERROR_FILE_ACCESS_DENIED)
|
|
|
|
.WithMessage("Could not get info for the file at %s",
|
|
|
|
NS_ConvertUTF16toUTF8(aPath).get()));
|
2020-07-30 17:02:34 +03:00
|
|
|
}
|
|
|
|
if (static_cast<uint64_t>(info.size) > UINT32_MAX) {
|
2020-07-30 17:02:50 +03:00
|
|
|
return Err(
|
|
|
|
IOError(NS_ERROR_FILE_TOO_BIG)
|
|
|
|
.WithMessage("Could not read the file at %s because it is too "
|
|
|
|
"large(size=%" PRIu64 " bytes)",
|
|
|
|
NS_ConvertUTF16toUTF8(aPath).get(),
|
|
|
|
static_cast<uint64_t>(info.size)));
|
2020-07-30 17:02:34 +03:00
|
|
|
}
|
|
|
|
bufSize = static_cast<uint32_t>(info.size);
|
|
|
|
} else {
|
|
|
|
bufSize = aMaxBytes.value();
|
|
|
|
}
|
|
|
|
|
2020-07-03 01:32:03 +03:00
|
|
|
nsTArray<uint8_t> buffer;
|
2020-07-30 17:02:34 +03:00
|
|
|
if (!buffer.SetCapacity(bufSize, fallible)) {
|
2020-07-30 17:02:50 +03:00
|
|
|
return Err(IOError(NS_ERROR_OUT_OF_MEMORY));
|
2020-07-03 01:32:03 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// If possible, advise the operating system that we will be reading the file
|
2020-07-30 17:02:34 +03:00
|
|
|
// pointed to by |fd| sequentially, in full. This advice is not binding, it
|
2020-07-03 01:32:03 +03:00
|
|
|
// informs the OS about our expectations as an application.
|
|
|
|
#if defined(HAVE_POSIX_FADVISE)
|
2020-07-30 17:02:34 +03:00
|
|
|
posix_fadvise(PR_FileDesc2NativeHandle(fd.get()), 0, 0,
|
|
|
|
POSIX_FADV_SEQUENTIAL);
|
2020-07-03 01:32:03 +03:00
|
|
|
#endif
|
|
|
|
|
|
|
|
uint32_t totalRead = 0;
|
2020-07-30 17:02:34 +03:00
|
|
|
while (totalRead != bufSize) {
|
2020-07-03 01:32:03 +03:00
|
|
|
int32_t nRead =
|
2020-07-30 17:02:34 +03:00
|
|
|
PR_Read(fd.get(), buffer.Elements() + totalRead, bufSize - totalRead);
|
2020-07-03 01:32:03 +03:00
|
|
|
if (nRead == 0) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (nRead < 0) {
|
2020-08-10 19:00:32 +03:00
|
|
|
PRErrorCode errNo = PR_GetError();
|
|
|
|
const char* errName = PR_ErrorToName(errNo);
|
2020-07-30 17:02:50 +03:00
|
|
|
return Err(
|
|
|
|
IOError(NS_ERROR_UNEXPECTED)
|
2020-08-10 19:00:32 +03:00
|
|
|
.WithMessage(
|
|
|
|
"Encountered an unexpected error while reading file(%s)"
|
|
|
|
"(PRErrorCode: %s)",
|
|
|
|
NS_ConvertUTF16toUTF8(aPath).get(), errName));
|
2020-07-03 01:32:03 +03:00
|
|
|
}
|
|
|
|
totalRead += nRead;
|
|
|
|
DebugOnly<bool> success = buffer.SetLength(totalRead, fallible);
|
|
|
|
MOZ_ASSERT(success);
|
|
|
|
}
|
2020-07-30 17:02:34 +03:00
|
|
|
return std::move(buffer);
|
2020-07-30 00:51:48 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/* static */
|
2020-07-30 17:02:50 +03:00
|
|
|
Result<uint32_t, IOUtils::IOError> IOUtils::WriteAtomicSync(
|
Backed out 12 changesets (bug 1660328, bug 1660015, bug 1649595, bug 1649596, bug 1649593, bug 1659176, bug 1659839, bug 1659838, bug 1657663, bug 1657647, bug 1655460) for xpcshell perma failures. CLOSED TREE
Backed out changeset ff95badf90e3 (bug 1660328)
Backed out changeset a92f8525ab6f (bug 1659176)
Backed out changeset 8ca05470a0d5 (bug 1659839)
Backed out changeset 5de389b735d3 (bug 1649596)
Backed out changeset 73bdddd96664 (bug 1649595)
Backed out changeset 59800d609b55 (bug 1659838)
Backed out changeset 8aca41723313 (bug 1649593)
Backed out changeset dc0d90b3e135 (bug 1657647)
Backed out changeset e3dd5b6b4fbd (bug 1657663)
Backed out changeset f9c823fa14ba (bug 1657663)
Backed out changeset a5aecc7a6469 (bug 1655460)
Backed out changeset 21b64ef30e12 (bug 1660015)
2020-08-26 06:35:20 +03:00
|
|
|
const nsAString& aDestPath, const Buffer<uint8_t>& aByteArray,
|
2020-07-30 17:03:03 +03:00
|
|
|
const IOUtils::InternalWriteAtomicOpts& aOptions) {
|
2020-07-30 17:02:36 +03:00
|
|
|
MOZ_ASSERT(!NS_IsMainThread());
|
|
|
|
|
|
|
|
// Check if the file exists and test it against the noOverwrite option.
|
|
|
|
const bool& noOverwrite = aOptions.mNoOverwrite;
|
|
|
|
bool exists = false;
|
|
|
|
{
|
|
|
|
UniquePtr<PRFileDesc, PR_CloseDelete> fd =
|
|
|
|
OpenExistingSync(aDestPath, PR_RDONLY);
|
|
|
|
exists = !!fd;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (noOverwrite && exists) {
|
2020-07-30 17:02:50 +03:00
|
|
|
return Err(IOError(NS_ERROR_FILE_ALREADY_EXISTS)
|
|
|
|
.WithMessage("Refusing to overwrite the file at %s\n"
|
|
|
|
"Specify `noOverwrite: false` to allow "
|
|
|
|
"overwriting the destination",
|
|
|
|
NS_ConvertUTF16toUTF8(aDestPath).get()));
|
2020-07-30 17:02:36 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// If backupFile was specified, perform the backup as a move.
|
2020-07-30 17:03:03 +03:00
|
|
|
if (exists && aOptions.mBackupFile.isSome() &&
|
|
|
|
MoveSync(aDestPath, aOptions.mBackupFile.value(), noOverwrite).isErr()) {
|
2020-07-30 17:02:50 +03:00
|
|
|
return Err(
|
|
|
|
IOError(NS_ERROR_FILE_COPY_OR_MOVE_FAILED)
|
|
|
|
.WithMessage(
|
|
|
|
"Failed to backup the source file(%s) to %s",
|
|
|
|
NS_ConvertUTF16toUTF8(aDestPath).get(),
|
2020-07-30 17:03:03 +03:00
|
|
|
NS_ConvertUTF16toUTF8(aOptions.mBackupFile.value()).get()));
|
2020-07-30 17:02:36 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// If tmpPath was specified, we will write to there first, then perform
|
|
|
|
// a move to ensure the file ends up at the final requested destination.
|
|
|
|
nsAutoString tmpPath;
|
2020-07-30 17:03:03 +03:00
|
|
|
if (aOptions.mTmpPath.isSome()) {
|
|
|
|
tmpPath = aOptions.mTmpPath.value();
|
2020-07-30 17:02:36 +03:00
|
|
|
} else {
|
|
|
|
tmpPath = aDestPath;
|
|
|
|
}
|
|
|
|
// The data to be written to file might be larger than can be
|
|
|
|
// written in any single call, so we must truncate the file and
|
|
|
|
// set the write mode to append to the file.
|
|
|
|
int32_t flags = PR_WRONLY | PR_TRUNCATE | PR_APPEND;
|
|
|
|
if (aOptions.mFlush) {
|
|
|
|
flags |= PR_SYNC;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Try to perform the write and ensure that the file is closed before
|
|
|
|
// continuing.
|
|
|
|
uint32_t result = 0;
|
|
|
|
{
|
|
|
|
UniquePtr<PRFileDesc, PR_CloseDelete> fd = OpenExistingSync(tmpPath, flags);
|
|
|
|
if (!fd) {
|
|
|
|
fd = CreateFileSync(tmpPath, flags);
|
|
|
|
}
|
|
|
|
if (!fd) {
|
2020-07-30 17:02:50 +03:00
|
|
|
return Err(IOError(NS_ERROR_FILE_ACCESS_DENIED)
|
|
|
|
.WithMessage("Could not open the file at %s for writing",
|
|
|
|
NS_ConvertUTF16toUTF8(tmpPath).get()));
|
2020-07-30 17:02:36 +03:00
|
|
|
}
|
|
|
|
|
2020-07-30 17:02:50 +03:00
|
|
|
auto rv = WriteSync(fd.get(), NS_ConvertUTF16toUTF8(tmpPath), aByteArray);
|
2020-07-30 17:02:36 +03:00
|
|
|
if (rv.isErr()) {
|
2020-07-30 17:02:50 +03:00
|
|
|
return rv.propagateErr();
|
2020-07-30 17:02:36 +03:00
|
|
|
}
|
|
|
|
result = rv.unwrap();
|
|
|
|
}
|
|
|
|
|
|
|
|
// If tmpPath was specified and different from the destPath, then the
|
|
|
|
// operation is finished by performing a move.
|
2020-07-30 17:02:38 +03:00
|
|
|
if (aDestPath != tmpPath && MoveSync(tmpPath, aDestPath, false).isErr()) {
|
2020-07-30 17:02:50 +03:00
|
|
|
return Err(
|
|
|
|
IOError(NS_ERROR_FILE_COPY_OR_MOVE_FAILED)
|
|
|
|
.WithMessage("Could not move temporary file(%s) to destination(%s)",
|
|
|
|
NS_ConvertUTF16toUTF8(tmpPath).get(),
|
|
|
|
NS_ConvertUTF16toUTF8(aDestPath).get()));
|
2020-07-30 17:02:36 +03:00
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* static */
|
2020-07-30 17:02:50 +03:00
|
|
|
Result<uint32_t, IOUtils::IOError> IOUtils::WriteSync(
|
Backed out 12 changesets (bug 1660328, bug 1660015, bug 1649595, bug 1649596, bug 1649593, bug 1659176, bug 1659839, bug 1659838, bug 1657663, bug 1657647, bug 1655460) for xpcshell perma failures. CLOSED TREE
Backed out changeset ff95badf90e3 (bug 1660328)
Backed out changeset a92f8525ab6f (bug 1659176)
Backed out changeset 8ca05470a0d5 (bug 1659839)
Backed out changeset 5de389b735d3 (bug 1649596)
Backed out changeset 73bdddd96664 (bug 1649595)
Backed out changeset 59800d609b55 (bug 1659838)
Backed out changeset 8aca41723313 (bug 1649593)
Backed out changeset dc0d90b3e135 (bug 1657647)
Backed out changeset e3dd5b6b4fbd (bug 1657663)
Backed out changeset f9c823fa14ba (bug 1657663)
Backed out changeset a5aecc7a6469 (bug 1655460)
Backed out changeset 21b64ef30e12 (bug 1660015)
2020-08-26 06:35:20 +03:00
|
|
|
PRFileDesc* aFd, const nsACString& aPath, const Buffer<uint8_t>& aBytes) {
|
2020-07-30 17:02:50 +03:00
|
|
|
// aBytes comes from a JavaScript TypedArray, which has UINT32_MAX max
|
|
|
|
// length.
|
2020-07-03 01:32:03 +03:00
|
|
|
MOZ_ASSERT(aBytes.Length() <= UINT32_MAX);
|
|
|
|
MOZ_ASSERT(!NS_IsMainThread());
|
|
|
|
|
|
|
|
if (aBytes.Length() == 0) {
|
2020-07-30 17:02:36 +03:00
|
|
|
return 0;
|
2020-07-03 01:32:03 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t bytesWritten = 0;
|
|
|
|
|
|
|
|
// PR_Write can only write up to PR_INT32_MAX bytes at a time, but the data
|
|
|
|
// source might be as large as UINT32_MAX bytes.
|
|
|
|
uint32_t chunkSize = PR_INT32_MAX;
|
Backed out 12 changesets (bug 1660328, bug 1660015, bug 1649595, bug 1649596, bug 1649593, bug 1659176, bug 1659839, bug 1659838, bug 1657663, bug 1657647, bug 1655460) for xpcshell perma failures. CLOSED TREE
Backed out changeset ff95badf90e3 (bug 1660328)
Backed out changeset a92f8525ab6f (bug 1659176)
Backed out changeset 8ca05470a0d5 (bug 1659839)
Backed out changeset 5de389b735d3 (bug 1649596)
Backed out changeset 73bdddd96664 (bug 1649595)
Backed out changeset 59800d609b55 (bug 1659838)
Backed out changeset 8aca41723313 (bug 1649593)
Backed out changeset dc0d90b3e135 (bug 1657647)
Backed out changeset e3dd5b6b4fbd (bug 1657663)
Backed out changeset f9c823fa14ba (bug 1657663)
Backed out changeset a5aecc7a6469 (bug 1655460)
Backed out changeset 21b64ef30e12 (bug 1660015)
2020-08-26 06:35:20 +03:00
|
|
|
uint32_t pendingBytes = aBytes.Length();
|
2020-07-03 01:32:03 +03:00
|
|
|
|
Backed out 12 changesets (bug 1660328, bug 1660015, bug 1649595, bug 1649596, bug 1649593, bug 1659176, bug 1659839, bug 1659838, bug 1657663, bug 1657647, bug 1655460) for xpcshell perma failures. CLOSED TREE
Backed out changeset ff95badf90e3 (bug 1660328)
Backed out changeset a92f8525ab6f (bug 1659176)
Backed out changeset 8ca05470a0d5 (bug 1659839)
Backed out changeset 5de389b735d3 (bug 1649596)
Backed out changeset 73bdddd96664 (bug 1649595)
Backed out changeset 59800d609b55 (bug 1659838)
Backed out changeset 8aca41723313 (bug 1649593)
Backed out changeset dc0d90b3e135 (bug 1657647)
Backed out changeset e3dd5b6b4fbd (bug 1657663)
Backed out changeset f9c823fa14ba (bug 1657663)
Backed out changeset a5aecc7a6469 (bug 1655460)
Backed out changeset 21b64ef30e12 (bug 1660015)
2020-08-26 06:35:20 +03:00
|
|
|
while (pendingBytes > 0) {
|
|
|
|
if (pendingBytes < chunkSize) {
|
|
|
|
chunkSize = pendingBytes;
|
2020-07-03 01:32:03 +03:00
|
|
|
}
|
Backed out 12 changesets (bug 1660328, bug 1660015, bug 1649595, bug 1649596, bug 1649593, bug 1659176, bug 1659839, bug 1659838, bug 1657663, bug 1657647, bug 1655460) for xpcshell perma failures. CLOSED TREE
Backed out changeset ff95badf90e3 (bug 1660328)
Backed out changeset a92f8525ab6f (bug 1659176)
Backed out changeset 8ca05470a0d5 (bug 1659839)
Backed out changeset 5de389b735d3 (bug 1649596)
Backed out changeset 73bdddd96664 (bug 1649595)
Backed out changeset 59800d609b55 (bug 1659838)
Backed out changeset 8aca41723313 (bug 1649593)
Backed out changeset dc0d90b3e135 (bug 1657647)
Backed out changeset e3dd5b6b4fbd (bug 1657663)
Backed out changeset f9c823fa14ba (bug 1657663)
Backed out changeset a5aecc7a6469 (bug 1655460)
Backed out changeset 21b64ef30e12 (bug 1660015)
2020-08-26 06:35:20 +03:00
|
|
|
int32_t rv = PR_Write(aFd, aBytes.begin() + bytesWritten, chunkSize);
|
2020-07-03 01:32:03 +03:00
|
|
|
if (rv < 0) {
|
2020-07-30 17:02:50 +03:00
|
|
|
return Err(IOError(NS_ERROR_FILE_CORRUPTED)
|
|
|
|
.WithMessage("Could not write chunk(size=%" PRIu32
|
|
|
|
") to %s\n"
|
|
|
|
"The file may be corrupt",
|
|
|
|
chunkSize, aPath.BeginReading()));
|
2020-07-03 01:32:03 +03:00
|
|
|
}
|
Backed out 12 changesets (bug 1660328, bug 1660015, bug 1649595, bug 1649596, bug 1649593, bug 1659176, bug 1659839, bug 1659838, bug 1657663, bug 1657647, bug 1655460) for xpcshell perma failures. CLOSED TREE
Backed out changeset ff95badf90e3 (bug 1660328)
Backed out changeset a92f8525ab6f (bug 1659176)
Backed out changeset 8ca05470a0d5 (bug 1659839)
Backed out changeset 5de389b735d3 (bug 1649596)
Backed out changeset 73bdddd96664 (bug 1649595)
Backed out changeset 59800d609b55 (bug 1659838)
Backed out changeset 8aca41723313 (bug 1649593)
Backed out changeset dc0d90b3e135 (bug 1657647)
Backed out changeset e3dd5b6b4fbd (bug 1657663)
Backed out changeset f9c823fa14ba (bug 1657663)
Backed out changeset a5aecc7a6469 (bug 1655460)
Backed out changeset 21b64ef30e12 (bug 1660015)
2020-08-26 06:35:20 +03:00
|
|
|
pendingBytes -= rv;
|
2020-07-03 01:32:03 +03:00
|
|
|
bytesWritten += rv;
|
|
|
|
}
|
|
|
|
|
2020-07-30 17:02:36 +03:00
|
|
|
return bytesWritten;
|
2020-07-03 01:32:03 +03:00
|
|
|
}
|
|
|
|
|
Bug 1650227: Implement IOUtils move method r=barret,Gijs
This patch introduces a move method to the IOUtils interface, which allows
for renaming/moving files or directories on disk. Source and destination
files may be specified either by an absolute path, or a relative path from
the current working directory.
This method has well-defined behaviour similar to the POSIX mv command
(except that this may create missing directories as necessary).
The behaviour is briefly summarized below:
1. If the source is a file that exists:
a. If the destination is a file that does not exist, the source is
renamed (and re-parented as a child of the destination parent
directory). The destination parent directory will be created if
necessary.
b. If the destination is a file that does exist, the destination is
replaced with the source (unless the noOverwrite option is true).
2. If the source is a directory that exists:
a. If the destination is a directory, then the source directory is
re-parented such that it becomes a child of the destination.
b. If the destination does not exist, then the source is renamed,
creating additional directories if needed.
c. If the destination is a file, then an error occurs.
3. If the source does not exist, an error occurs.
Differential Revision: https://phabricator.services.mozilla.com/D82202
2020-07-15 19:03:52 +03:00
|
|
|
/* static */
|
2020-07-30 17:02:50 +03:00
|
|
|
Result<Ok, IOUtils::IOError> IOUtils::MoveSync(const nsAString& aSourcePath,
|
|
|
|
const nsAString& aDestPath,
|
2020-08-10 19:00:32 +03:00
|
|
|
bool aNoOverwrite) {
|
Bug 1650227: Implement IOUtils move method r=barret,Gijs
This patch introduces a move method to the IOUtils interface, which allows
for renaming/moving files or directories on disk. Source and destination
files may be specified either by an absolute path, or a relative path from
the current working directory.
This method has well-defined behaviour similar to the POSIX mv command
(except that this may create missing directories as necessary).
The behaviour is briefly summarized below:
1. If the source is a file that exists:
a. If the destination is a file that does not exist, the source is
renamed (and re-parented as a child of the destination parent
directory). The destination parent directory will be created if
necessary.
b. If the destination is a file that does exist, the destination is
replaced with the source (unless the noOverwrite option is true).
2. If the source is a directory that exists:
a. If the destination is a directory, then the source directory is
re-parented such that it becomes a child of the destination.
b. If the destination does not exist, then the source is renamed,
creating additional directories if needed.
c. If the destination is a file, then an error occurs.
3. If the source does not exist, an error occurs.
Differential Revision: https://phabricator.services.mozilla.com/D82202
2020-07-15 19:03:52 +03:00
|
|
|
MOZ_ASSERT(!NS_IsMainThread());
|
|
|
|
|
2020-08-10 19:00:32 +03:00
|
|
|
RefPtr<nsLocalFile> srcFile = new nsLocalFile();
|
Bug 1650227: Implement IOUtils move method r=barret,Gijs
This patch introduces a move method to the IOUtils interface, which allows
for renaming/moving files or directories on disk. Source and destination
files may be specified either by an absolute path, or a relative path from
the current working directory.
This method has well-defined behaviour similar to the POSIX mv command
(except that this may create missing directories as necessary).
The behaviour is briefly summarized below:
1. If the source is a file that exists:
a. If the destination is a file that does not exist, the source is
renamed (and re-parented as a child of the destination parent
directory). The destination parent directory will be created if
necessary.
b. If the destination is a file that does exist, the destination is
replaced with the source (unless the noOverwrite option is true).
2. If the source is a directory that exists:
a. If the destination is a directory, then the source directory is
re-parented such that it becomes a child of the destination.
b. If the destination does not exist, then the source is renamed,
creating additional directories if needed.
c. If the destination is a file, then an error occurs.
3. If the source does not exist, an error occurs.
Differential Revision: https://phabricator.services.mozilla.com/D82202
2020-07-15 19:03:52 +03:00
|
|
|
MOZ_TRY(srcFile->InitWithPath(aSourcePath)); // Fails if not absolute.
|
2020-08-10 19:00:32 +03:00
|
|
|
|
|
|
|
// Ensure the source file exists before continuing. If it doesn't exist,
|
|
|
|
// subsequent operations can fail in different ways on different platforms.
|
2020-07-30 17:02:50 +03:00
|
|
|
bool srcExists;
|
|
|
|
MOZ_TRY(srcFile->Exists(&srcExists));
|
|
|
|
if (!srcExists) {
|
|
|
|
return Err(
|
|
|
|
IOError(NS_ERROR_FILE_NOT_FOUND)
|
|
|
|
.WithMessage(
|
|
|
|
"Could not move source file(%s) because it does not exist",
|
|
|
|
NS_ConvertUTF16toUTF8(aSourcePath).get()));
|
|
|
|
}
|
|
|
|
|
2020-08-10 19:00:32 +03:00
|
|
|
RefPtr<nsLocalFile> destFile = new nsLocalFile();
|
2020-07-30 17:02:50 +03:00
|
|
|
MOZ_TRY(destFile->InitWithPath(aDestPath)); // Fails if not absolute.
|
2020-08-10 19:00:32 +03:00
|
|
|
|
|
|
|
return CopyOrMoveSync(&nsLocalFile::MoveTo, "move", srcFile, destFile,
|
|
|
|
aNoOverwrite);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* static */
|
|
|
|
Result<Ok, IOUtils::IOError> IOUtils::CopySync(const nsAString& aSourcePath,
|
|
|
|
const nsAString& aDestPath,
|
|
|
|
bool aNoOverwrite,
|
|
|
|
bool aRecursive) {
|
|
|
|
MOZ_ASSERT(!NS_IsMainThread());
|
|
|
|
|
|
|
|
RefPtr<nsLocalFile> srcFile = new nsLocalFile();
|
|
|
|
MOZ_TRY(srcFile->InitWithPath(aSourcePath)); // Fails if not absolute.
|
|
|
|
|
|
|
|
// Ensure the source file exists before continuing. If it doesn't exist,
|
|
|
|
// subsequent operations can fail in different ways on different platforms.
|
|
|
|
bool srcExists;
|
|
|
|
MOZ_TRY(srcFile->Exists(&srcExists));
|
|
|
|
if (!srcExists) {
|
|
|
|
return Err(
|
|
|
|
IOError(NS_ERROR_FILE_NOT_FOUND)
|
|
|
|
.WithMessage(
|
|
|
|
"Could not copy source file(%s) because it does not exist",
|
|
|
|
NS_ConvertUTF16toUTF8(aSourcePath).get()));
|
|
|
|
}
|
|
|
|
|
|
|
|
// If source is a directory, fail immediately unless the recursive option is
|
|
|
|
// true.
|
|
|
|
bool srcIsDir = false;
|
|
|
|
MOZ_TRY(srcFile->IsDirectory(&srcIsDir));
|
|
|
|
if (srcIsDir && !aRecursive) {
|
|
|
|
return Err(
|
|
|
|
IOError(NS_ERROR_FILE_COPY_OR_MOVE_FAILED)
|
|
|
|
.WithMessage(
|
|
|
|
"Refused to copy source directory(%s) to the destination(%s)\n"
|
|
|
|
"Specify the `recursive: true` option to allow copying "
|
|
|
|
"directories",
|
|
|
|
NS_ConvertUTF16toUTF8(aSourcePath).get(),
|
|
|
|
NS_ConvertUTF16toUTF8(aDestPath).get()));
|
|
|
|
}
|
|
|
|
|
|
|
|
RefPtr<nsLocalFile> destFile = new nsLocalFile();
|
|
|
|
MOZ_TRY(destFile->InitWithPath(aDestPath)); // Fails if not absolute.
|
|
|
|
|
|
|
|
return CopyOrMoveSync(&nsLocalFile::CopyTo, "copy", srcFile, destFile,
|
|
|
|
aNoOverwrite);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* static */
|
|
|
|
template <typename CopyOrMoveFn>
|
|
|
|
Result<Ok, IOUtils::IOError> IOUtils::CopyOrMoveSync(
|
|
|
|
CopyOrMoveFn aMethod, const char* aMethodName,
|
|
|
|
const RefPtr<nsLocalFile>& aSource, const RefPtr<nsLocalFile>& aDest,
|
|
|
|
bool aNoOverwrite) {
|
Backed out 12 changesets (bug 1660328, bug 1660015, bug 1649595, bug 1649596, bug 1649593, bug 1659176, bug 1659839, bug 1659838, bug 1657663, bug 1657647, bug 1655460) for xpcshell perma failures. CLOSED TREE
Backed out changeset ff95badf90e3 (bug 1660328)
Backed out changeset a92f8525ab6f (bug 1659176)
Backed out changeset 8ca05470a0d5 (bug 1659839)
Backed out changeset 5de389b735d3 (bug 1649596)
Backed out changeset 73bdddd96664 (bug 1649595)
Backed out changeset 59800d609b55 (bug 1659838)
Backed out changeset 8aca41723313 (bug 1649593)
Backed out changeset dc0d90b3e135 (bug 1657647)
Backed out changeset e3dd5b6b4fbd (bug 1657663)
Backed out changeset f9c823fa14ba (bug 1657663)
Backed out changeset a5aecc7a6469 (bug 1655460)
Backed out changeset 21b64ef30e12 (bug 1660015)
2020-08-26 06:35:20 +03:00
|
|
|
nsresult rv = NS_OK;
|
2020-08-10 19:00:32 +03:00
|
|
|
|
|
|
|
// Normalize the file paths.
|
|
|
|
MOZ_TRY(aSource->Normalize());
|
Backed out 12 changesets (bug 1660328, bug 1660015, bug 1649595, bug 1649596, bug 1649593, bug 1659176, bug 1659839, bug 1659838, bug 1657663, bug 1657647, bug 1655460) for xpcshell perma failures. CLOSED TREE
Backed out changeset ff95badf90e3 (bug 1660328)
Backed out changeset a92f8525ab6f (bug 1659176)
Backed out changeset 8ca05470a0d5 (bug 1659839)
Backed out changeset 5de389b735d3 (bug 1649596)
Backed out changeset 73bdddd96664 (bug 1649595)
Backed out changeset 59800d609b55 (bug 1659838)
Backed out changeset 8aca41723313 (bug 1649593)
Backed out changeset dc0d90b3e135 (bug 1657647)
Backed out changeset e3dd5b6b4fbd (bug 1657663)
Backed out changeset f9c823fa14ba (bug 1657663)
Backed out changeset a5aecc7a6469 (bug 1655460)
Backed out changeset 21b64ef30e12 (bug 1660015)
2020-08-26 06:35:20 +03:00
|
|
|
rv = aDest->Normalize();
|
Bug 1650227: Implement IOUtils move method r=barret,Gijs
This patch introduces a move method to the IOUtils interface, which allows
for renaming/moving files or directories on disk. Source and destination
files may be specified either by an absolute path, or a relative path from
the current working directory.
This method has well-defined behaviour similar to the POSIX mv command
(except that this may create missing directories as necessary).
The behaviour is briefly summarized below:
1. If the source is a file that exists:
a. If the destination is a file that does not exist, the source is
renamed (and re-parented as a child of the destination parent
directory). The destination parent directory will be created if
necessary.
b. If the destination is a file that does exist, the destination is
replaced with the source (unless the noOverwrite option is true).
2. If the source is a directory that exists:
a. If the destination is a directory, then the source directory is
re-parented such that it becomes a child of the destination.
b. If the destination does not exist, then the source is renamed,
creating additional directories if needed.
c. If the destination is a file, then an error occurs.
3. If the source does not exist, an error occurs.
Differential Revision: https://phabricator.services.mozilla.com/D82202
2020-07-15 19:03:52 +03:00
|
|
|
// Normalize can fail for a number of reasons, including if the file doesn't
|
|
|
|
// exist. It is expected that the file might not exist for a number of calls
|
2020-08-10 19:00:32 +03:00
|
|
|
// (e.g. if we want to copy or move a file to a new location).
|
2020-07-30 17:02:38 +03:00
|
|
|
if (NS_FAILED(rv) && !IsFileNotFound(rv)) {
|
|
|
|
// Deliberately ignore "not found" errors, but propagate all others.
|
2020-07-30 17:02:50 +03:00
|
|
|
return Err(IOError(rv));
|
Bug 1650227: Implement IOUtils move method r=barret,Gijs
This patch introduces a move method to the IOUtils interface, which allows
for renaming/moving files or directories on disk. Source and destination
files may be specified either by an absolute path, or a relative path from
the current working directory.
This method has well-defined behaviour similar to the POSIX mv command
(except that this may create missing directories as necessary).
The behaviour is briefly summarized below:
1. If the source is a file that exists:
a. If the destination is a file that does not exist, the source is
renamed (and re-parented as a child of the destination parent
directory). The destination parent directory will be created if
necessary.
b. If the destination is a file that does exist, the destination is
replaced with the source (unless the noOverwrite option is true).
2. If the source is a directory that exists:
a. If the destination is a directory, then the source directory is
re-parented such that it becomes a child of the destination.
b. If the destination does not exist, then the source is renamed,
creating additional directories if needed.
c. If the destination is a file, then an error occurs.
3. If the source does not exist, an error occurs.
Differential Revision: https://phabricator.services.mozilla.com/D82202
2020-07-15 19:03:52 +03:00
|
|
|
}
|
|
|
|
|
2020-08-10 19:00:32 +03:00
|
|
|
nsAutoString src;
|
|
|
|
MOZ_TRY(aSource->GetPath(src));
|
|
|
|
auto sourcePath = NS_ConvertUTF16toUTF8(src);
|
|
|
|
nsAutoString dest;
|
|
|
|
MOZ_TRY(aDest->GetPath(dest));
|
|
|
|
auto destPath = NS_ConvertUTF16toUTF8(dest);
|
|
|
|
|
|
|
|
// Case 1: Destination is an existing directory. Copy/move source into dest.
|
Bug 1650227: Implement IOUtils move method r=barret,Gijs
This patch introduces a move method to the IOUtils interface, which allows
for renaming/moving files or directories on disk. Source and destination
files may be specified either by an absolute path, or a relative path from
the current working directory.
This method has well-defined behaviour similar to the POSIX mv command
(except that this may create missing directories as necessary).
The behaviour is briefly summarized below:
1. If the source is a file that exists:
a. If the destination is a file that does not exist, the source is
renamed (and re-parented as a child of the destination parent
directory). The destination parent directory will be created if
necessary.
b. If the destination is a file that does exist, the destination is
replaced with the source (unless the noOverwrite option is true).
2. If the source is a directory that exists:
a. If the destination is a directory, then the source directory is
re-parented such that it becomes a child of the destination.
b. If the destination does not exist, then the source is renamed,
creating additional directories if needed.
c. If the destination is a file, then an error occurs.
3. If the source does not exist, an error occurs.
Differential Revision: https://phabricator.services.mozilla.com/D82202
2020-07-15 19:03:52 +03:00
|
|
|
bool destIsDir = false;
|
|
|
|
bool destExists = true;
|
|
|
|
|
2020-08-10 19:00:32 +03:00
|
|
|
rv = aDest->IsDirectory(&destIsDir);
|
Bug 1650227: Implement IOUtils move method r=barret,Gijs
This patch introduces a move method to the IOUtils interface, which allows
for renaming/moving files or directories on disk. Source and destination
files may be specified either by an absolute path, or a relative path from
the current working directory.
This method has well-defined behaviour similar to the POSIX mv command
(except that this may create missing directories as necessary).
The behaviour is briefly summarized below:
1. If the source is a file that exists:
a. If the destination is a file that does not exist, the source is
renamed (and re-parented as a child of the destination parent
directory). The destination parent directory will be created if
necessary.
b. If the destination is a file that does exist, the destination is
replaced with the source (unless the noOverwrite option is true).
2. If the source is a directory that exists:
a. If the destination is a directory, then the source directory is
re-parented such that it becomes a child of the destination.
b. If the destination does not exist, then the source is renamed,
creating additional directories if needed.
c. If the destination is a file, then an error occurs.
3. If the source does not exist, an error occurs.
Differential Revision: https://phabricator.services.mozilla.com/D82202
2020-07-15 19:03:52 +03:00
|
|
|
if (NS_SUCCEEDED(rv) && destIsDir) {
|
2020-08-10 19:00:32 +03:00
|
|
|
rv = (aSource->*aMethod)(aDest, EmptyString());
|
2020-07-30 17:02:50 +03:00
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
return Err(IOError(rv).WithMessage(
|
2020-08-10 19:00:32 +03:00
|
|
|
"Could not %s source file(%s) to destination directory(%s)",
|
|
|
|
aMethodName, sourcePath.get(), destPath.get()));
|
2020-07-30 17:02:50 +03:00
|
|
|
}
|
|
|
|
return Ok();
|
Bug 1650227: Implement IOUtils move method r=barret,Gijs
This patch introduces a move method to the IOUtils interface, which allows
for renaming/moving files or directories on disk. Source and destination
files may be specified either by an absolute path, or a relative path from
the current working directory.
This method has well-defined behaviour similar to the POSIX mv command
(except that this may create missing directories as necessary).
The behaviour is briefly summarized below:
1. If the source is a file that exists:
a. If the destination is a file that does not exist, the source is
renamed (and re-parented as a child of the destination parent
directory). The destination parent directory will be created if
necessary.
b. If the destination is a file that does exist, the destination is
replaced with the source (unless the noOverwrite option is true).
2. If the source is a directory that exists:
a. If the destination is a directory, then the source directory is
re-parented such that it becomes a child of the destination.
b. If the destination does not exist, then the source is renamed,
creating additional directories if needed.
c. If the destination is a file, then an error occurs.
3. If the source does not exist, an error occurs.
Differential Revision: https://phabricator.services.mozilla.com/D82202
2020-07-15 19:03:52 +03:00
|
|
|
}
|
2020-07-30 17:02:50 +03:00
|
|
|
|
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
if (!IsFileNotFound(rv)) {
|
|
|
|
// It's ok if the dest file doesn't exist. Case 2 handles this below.
|
|
|
|
// Bail out early for any other kind of error though.
|
|
|
|
return Err(IOError(rv));
|
|
|
|
}
|
Bug 1650227: Implement IOUtils move method r=barret,Gijs
This patch introduces a move method to the IOUtils interface, which allows
for renaming/moving files or directories on disk. Source and destination
files may be specified either by an absolute path, or a relative path from
the current working directory.
This method has well-defined behaviour similar to the POSIX mv command
(except that this may create missing directories as necessary).
The behaviour is briefly summarized below:
1. If the source is a file that exists:
a. If the destination is a file that does not exist, the source is
renamed (and re-parented as a child of the destination parent
directory). The destination parent directory will be created if
necessary.
b. If the destination is a file that does exist, the destination is
replaced with the source (unless the noOverwrite option is true).
2. If the source is a directory that exists:
a. If the destination is a directory, then the source directory is
re-parented such that it becomes a child of the destination.
b. If the destination does not exist, then the source is renamed,
creating additional directories if needed.
c. If the destination is a file, then an error occurs.
3. If the source does not exist, an error occurs.
Differential Revision: https://phabricator.services.mozilla.com/D82202
2020-07-15 19:03:52 +03:00
|
|
|
destExists = false;
|
|
|
|
}
|
|
|
|
|
2020-08-10 19:00:32 +03:00
|
|
|
// Case 2: Destination is a file which may or may not exist.
|
|
|
|
// Try to copy or rename the source to the destination.
|
|
|
|
// If the destination exists and the source is not a regular file,
|
|
|
|
// then this may fail.
|
|
|
|
if (aNoOverwrite && destExists) {
|
2020-07-30 17:02:50 +03:00
|
|
|
return Err(
|
|
|
|
IOError(NS_ERROR_FILE_ALREADY_EXISTS)
|
|
|
|
.WithMessage(
|
2020-08-10 19:00:32 +03:00
|
|
|
"Could not %s source file(%s) to destination(%s) because the "
|
2020-07-30 17:02:50 +03:00
|
|
|
"destination already exists and overwrites are not allowed\n"
|
|
|
|
"Specify the `noOverwrite: false` option to mitigate this "
|
|
|
|
"error",
|
2020-08-10 19:00:32 +03:00
|
|
|
aMethodName, sourcePath.get(), destPath.get()));
|
Bug 1650227: Implement IOUtils move method r=barret,Gijs
This patch introduces a move method to the IOUtils interface, which allows
for renaming/moving files or directories on disk. Source and destination
files may be specified either by an absolute path, or a relative path from
the current working directory.
This method has well-defined behaviour similar to the POSIX mv command
(except that this may create missing directories as necessary).
The behaviour is briefly summarized below:
1. If the source is a file that exists:
a. If the destination is a file that does not exist, the source is
renamed (and re-parented as a child of the destination parent
directory). The destination parent directory will be created if
necessary.
b. If the destination is a file that does exist, the destination is
replaced with the source (unless the noOverwrite option is true).
2. If the source is a directory that exists:
a. If the destination is a directory, then the source directory is
re-parented such that it becomes a child of the destination.
b. If the destination does not exist, then the source is renamed,
creating additional directories if needed.
c. If the destination is a file, then an error occurs.
3. If the source does not exist, an error occurs.
Differential Revision: https://phabricator.services.mozilla.com/D82202
2020-07-15 19:03:52 +03:00
|
|
|
}
|
|
|
|
if (destExists && !destIsDir) {
|
|
|
|
// If the source file is a directory, but the target is a file, abort early.
|
2020-08-10 19:00:32 +03:00
|
|
|
// Different implementations of |CopyTo| and |MoveTo| seem to handle this
|
|
|
|
// error case differently (or not at all), so we explicitly handle it here.
|
Bug 1650227: Implement IOUtils move method r=barret,Gijs
This patch introduces a move method to the IOUtils interface, which allows
for renaming/moving files or directories on disk. Source and destination
files may be specified either by an absolute path, or a relative path from
the current working directory.
This method has well-defined behaviour similar to the POSIX mv command
(except that this may create missing directories as necessary).
The behaviour is briefly summarized below:
1. If the source is a file that exists:
a. If the destination is a file that does not exist, the source is
renamed (and re-parented as a child of the destination parent
directory). The destination parent directory will be created if
necessary.
b. If the destination is a file that does exist, the destination is
replaced with the source (unless the noOverwrite option is true).
2. If the source is a directory that exists:
a. If the destination is a directory, then the source directory is
re-parented such that it becomes a child of the destination.
b. If the destination does not exist, then the source is renamed,
creating additional directories if needed.
c. If the destination is a file, then an error occurs.
3. If the source does not exist, an error occurs.
Differential Revision: https://phabricator.services.mozilla.com/D82202
2020-07-15 19:03:52 +03:00
|
|
|
bool srcIsDir = false;
|
2020-08-10 19:00:32 +03:00
|
|
|
MOZ_TRY(aSource->IsDirectory(&srcIsDir));
|
Bug 1650227: Implement IOUtils move method r=barret,Gijs
This patch introduces a move method to the IOUtils interface, which allows
for renaming/moving files or directories on disk. Source and destination
files may be specified either by an absolute path, or a relative path from
the current working directory.
This method has well-defined behaviour similar to the POSIX mv command
(except that this may create missing directories as necessary).
The behaviour is briefly summarized below:
1. If the source is a file that exists:
a. If the destination is a file that does not exist, the source is
renamed (and re-parented as a child of the destination parent
directory). The destination parent directory will be created if
necessary.
b. If the destination is a file that does exist, the destination is
replaced with the source (unless the noOverwrite option is true).
2. If the source is a directory that exists:
a. If the destination is a directory, then the source directory is
re-parented such that it becomes a child of the destination.
b. If the destination does not exist, then the source is renamed,
creating additional directories if needed.
c. If the destination is a file, then an error occurs.
3. If the source does not exist, an error occurs.
Differential Revision: https://phabricator.services.mozilla.com/D82202
2020-07-15 19:03:52 +03:00
|
|
|
if (srcIsDir) {
|
2020-07-30 17:02:50 +03:00
|
|
|
return Err(IOError(NS_ERROR_FILE_DESTINATION_NOT_DIR)
|
2020-08-10 19:00:32 +03:00
|
|
|
.WithMessage("Could not %s the source directory(%s) to "
|
|
|
|
"the destination(%s) because the destination "
|
|
|
|
"is not a directory",
|
|
|
|
aMethodName, sourcePath.get(),
|
|
|
|
destPath.get()));
|
Bug 1650227: Implement IOUtils move method r=barret,Gijs
This patch introduces a move method to the IOUtils interface, which allows
for renaming/moving files or directories on disk. Source and destination
files may be specified either by an absolute path, or a relative path from
the current working directory.
This method has well-defined behaviour similar to the POSIX mv command
(except that this may create missing directories as necessary).
The behaviour is briefly summarized below:
1. If the source is a file that exists:
a. If the destination is a file that does not exist, the source is
renamed (and re-parented as a child of the destination parent
directory). The destination parent directory will be created if
necessary.
b. If the destination is a file that does exist, the destination is
replaced with the source (unless the noOverwrite option is true).
2. If the source is a directory that exists:
a. If the destination is a directory, then the source directory is
re-parented such that it becomes a child of the destination.
b. If the destination does not exist, then the source is renamed,
creating additional directories if needed.
c. If the destination is a file, then an error occurs.
3. If the source does not exist, an error occurs.
Differential Revision: https://phabricator.services.mozilla.com/D82202
2020-07-15 19:03:52 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
nsCOMPtr<nsIFile> destDir;
|
|
|
|
nsAutoString destName;
|
2020-08-10 19:00:32 +03:00
|
|
|
MOZ_TRY(aDest->GetLeafName(destName));
|
|
|
|
MOZ_TRY(aDest->GetParent(getter_AddRefs(destDir)));
|
Bug 1650227: Implement IOUtils move method r=barret,Gijs
This patch introduces a move method to the IOUtils interface, which allows
for renaming/moving files or directories on disk. Source and destination
files may be specified either by an absolute path, or a relative path from
the current working directory.
This method has well-defined behaviour similar to the POSIX mv command
(except that this may create missing directories as necessary).
The behaviour is briefly summarized below:
1. If the source is a file that exists:
a. If the destination is a file that does not exist, the source is
renamed (and re-parented as a child of the destination parent
directory). The destination parent directory will be created if
necessary.
b. If the destination is a file that does exist, the destination is
replaced with the source (unless the noOverwrite option is true).
2. If the source is a directory that exists:
a. If the destination is a directory, then the source directory is
re-parented such that it becomes a child of the destination.
b. If the destination does not exist, then the source is renamed,
creating additional directories if needed.
c. If the destination is a file, then an error occurs.
3. If the source does not exist, an error occurs.
Differential Revision: https://phabricator.services.mozilla.com/D82202
2020-07-15 19:03:52 +03:00
|
|
|
|
2020-08-10 19:00:32 +03:00
|
|
|
// NB: if destDir doesn't exist, then |CopyTo| or |MoveTo| will create it.
|
|
|
|
rv = (aSource->*aMethod)(destDir, destName);
|
2020-07-30 17:02:50 +03:00
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
return Err(IOError(rv).WithMessage(
|
2020-08-10 19:00:32 +03:00
|
|
|
"Could not %s the source file(%s) to the destination(%s)", aMethodName,
|
|
|
|
sourcePath.get(), destPath.get()));
|
2020-07-30 17:02:50 +03:00
|
|
|
}
|
|
|
|
return Ok();
|
Bug 1650227: Implement IOUtils move method r=barret,Gijs
This patch introduces a move method to the IOUtils interface, which allows
for renaming/moving files or directories on disk. Source and destination
files may be specified either by an absolute path, or a relative path from
the current working directory.
This method has well-defined behaviour similar to the POSIX mv command
(except that this may create missing directories as necessary).
The behaviour is briefly summarized below:
1. If the source is a file that exists:
a. If the destination is a file that does not exist, the source is
renamed (and re-parented as a child of the destination parent
directory). The destination parent directory will be created if
necessary.
b. If the destination is a file that does exist, the destination is
replaced with the source (unless the noOverwrite option is true).
2. If the source is a directory that exists:
a. If the destination is a directory, then the source directory is
re-parented such that it becomes a child of the destination.
b. If the destination does not exist, then the source is renamed,
creating additional directories if needed.
c. If the destination is a file, then an error occurs.
3. If the source does not exist, an error occurs.
Differential Revision: https://phabricator.services.mozilla.com/D82202
2020-07-15 19:03:52 +03:00
|
|
|
}
|
|
|
|
|
2020-07-18 03:31:57 +03:00
|
|
|
/* static */
|
2020-07-30 17:02:50 +03:00
|
|
|
Result<Ok, IOUtils::IOError> IOUtils::RemoveSync(const nsAString& aPath,
|
|
|
|
bool aIgnoreAbsent,
|
|
|
|
bool aRecursive) {
|
2020-07-30 17:02:38 +03:00
|
|
|
MOZ_ASSERT(!NS_IsMainThread());
|
|
|
|
|
2020-07-18 03:31:57 +03:00
|
|
|
RefPtr<nsLocalFile> file = new nsLocalFile();
|
|
|
|
MOZ_TRY(file->InitWithPath(aPath));
|
|
|
|
|
|
|
|
nsresult rv = file->Remove(aRecursive);
|
|
|
|
if (aIgnoreAbsent && IsFileNotFound(rv)) {
|
2020-07-30 17:02:38 +03:00
|
|
|
return Ok();
|
2020-07-30 00:52:32 +03:00
|
|
|
}
|
2020-07-30 17:02:50 +03:00
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
IOError err(rv);
|
|
|
|
if (IsFileNotFound(rv)) {
|
|
|
|
return Err(err.WithMessage(
|
|
|
|
"Could not remove the file at %s because it does not exist.\n"
|
|
|
|
"Specify the `ignoreAbsent: true` option to mitigate this error",
|
|
|
|
NS_ConvertUTF16toUTF8(aPath).get()));
|
|
|
|
}
|
|
|
|
if (rv == NS_ERROR_FILE_DIR_NOT_EMPTY) {
|
|
|
|
return Err(err.WithMessage(
|
|
|
|
"Could not remove the non-empty directory at %s.\n"
|
|
|
|
"Specify the `recursive: true` option to mitigate this error",
|
|
|
|
NS_ConvertUTF16toUTF8(aPath).get()));
|
|
|
|
}
|
|
|
|
return Err(err.WithMessage("Could not remove the file at %s",
|
|
|
|
NS_ConvertUTF16toUTF8(aPath).get()));
|
|
|
|
}
|
|
|
|
return Ok();
|
2020-07-18 03:31:57 +03:00
|
|
|
}
|
|
|
|
|
2020-07-21 18:13:35 +03:00
|
|
|
/* static */
|
2020-07-30 17:02:50 +03:00
|
|
|
Result<Ok, IOUtils::IOError> IOUtils::CreateDirectorySync(
|
|
|
|
const nsAString& aPath, bool aCreateAncestors, bool aIgnoreExisting,
|
|
|
|
int32_t aMode) {
|
2020-07-30 17:02:38 +03:00
|
|
|
MOZ_ASSERT(!NS_IsMainThread());
|
|
|
|
|
2020-07-21 18:13:35 +03:00
|
|
|
RefPtr<nsLocalFile> targetFile = new nsLocalFile();
|
|
|
|
MOZ_TRY(targetFile->InitWithPath(aPath));
|
|
|
|
|
|
|
|
// nsIFile::Create will create ancestor directories by default.
|
2020-07-30 17:02:50 +03:00
|
|
|
// If the caller does not want this behaviour, then check and possibly
|
|
|
|
// return an error.
|
2020-07-21 18:13:35 +03:00
|
|
|
if (!aCreateAncestors) {
|
|
|
|
nsCOMPtr<nsIFile> parent;
|
|
|
|
MOZ_TRY(targetFile->GetParent(getter_AddRefs(parent)));
|
|
|
|
bool parentExists;
|
|
|
|
MOZ_TRY(parent->Exists(&parentExists));
|
|
|
|
if (!parentExists) {
|
2020-07-30 17:02:50 +03:00
|
|
|
return Err(IOError(NS_ERROR_FILE_NOT_FOUND)
|
|
|
|
.WithMessage("Could not create directory at %s because "
|
|
|
|
"the path has missing "
|
|
|
|
"ancestor components",
|
|
|
|
NS_ConvertUTF16toUTF8(aPath).get()));
|
2020-07-21 18:13:35 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult rv = targetFile->Create(nsIFile::DIRECTORY_TYPE, aMode);
|
2020-07-30 17:02:50 +03:00
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
IOError err(rv);
|
|
|
|
if (rv == NS_ERROR_FILE_ALREADY_EXISTS) {
|
|
|
|
// NB: We may report a success only if the target is an existing
|
|
|
|
// directory. We don't want to silence errors that occur if the target is
|
|
|
|
// an existing file, since trying to create a directory where a regular
|
|
|
|
// file exists may be indicative of a logic error.
|
|
|
|
bool isDirectory;
|
|
|
|
MOZ_TRY(targetFile->IsDirectory(&isDirectory));
|
|
|
|
if (!isDirectory) {
|
|
|
|
return Err(IOError(NS_ERROR_FILE_NOT_DIRECTORY)
|
|
|
|
.WithMessage("Could not create directory because the "
|
|
|
|
"target file(%s) exists "
|
|
|
|
"and is not a directory",
|
|
|
|
NS_ConvertUTF16toUTF8(aPath).get()));
|
|
|
|
}
|
|
|
|
// The directory exists.
|
|
|
|
// The caller may suppress this error.
|
|
|
|
if (aIgnoreExisting) {
|
|
|
|
return Ok();
|
|
|
|
}
|
|
|
|
// Otherwise, forward it.
|
|
|
|
return Err(err.WithMessage(
|
|
|
|
"Could not create directory because it already exists at %s\n"
|
|
|
|
"Specify the `ignoreExisting: true` option to mitigate this "
|
|
|
|
"error",
|
|
|
|
NS_ConvertUTF16toUTF8(aPath).get()));
|
2020-07-21 18:13:35 +03:00
|
|
|
}
|
2020-07-30 17:02:50 +03:00
|
|
|
return Err(err.WithMessage("Could not create directory at %s",
|
|
|
|
NS_ConvertUTF16toUTF8(aPath).get()));
|
2020-07-21 18:13:35 +03:00
|
|
|
}
|
2020-07-30 17:02:50 +03:00
|
|
|
return Ok();
|
2020-07-21 18:13:35 +03:00
|
|
|
}
|
|
|
|
|
2020-07-30 17:02:50 +03:00
|
|
|
Result<IOUtils::InternalFileInfo, IOUtils::IOError> IOUtils::StatSync(
|
2020-07-23 21:15:30 +03:00
|
|
|
const nsAString& aPath) {
|
|
|
|
MOZ_ASSERT(!NS_IsMainThread());
|
|
|
|
|
|
|
|
RefPtr<nsLocalFile> file = new nsLocalFile();
|
|
|
|
MOZ_TRY(file->InitWithPath(aPath));
|
|
|
|
|
|
|
|
InternalFileInfo info;
|
|
|
|
info.mPath = nsString(aPath);
|
|
|
|
|
2020-07-30 16:11:00 +03:00
|
|
|
bool isRegular;
|
2020-07-30 17:02:50 +03:00
|
|
|
// IsFile will stat and cache info in the file object. If the file doesn't
|
|
|
|
// exist, or there is an access error, we'll discover it here.
|
|
|
|
// Any subsequent errors are unexpected and will just be forwarded.
|
|
|
|
nsresult rv = file->IsFile(&isRegular);
|
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
IOError err(rv);
|
|
|
|
if (IsFileNotFound(rv)) {
|
|
|
|
return Err(
|
|
|
|
err.WithMessage("Could not stat file(%s) because it does not exist",
|
|
|
|
NS_ConvertUTF16toUTF8(aPath).get()));
|
|
|
|
}
|
|
|
|
return Err(err);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now we can populate the info object by querying the file.
|
|
|
|
info.mType = FileType::Regular;
|
2020-07-23 21:15:30 +03:00
|
|
|
if (!isRegular) {
|
|
|
|
bool isDir;
|
|
|
|
MOZ_TRY(file->IsDirectory(&isDir));
|
|
|
|
if (isDir) {
|
|
|
|
info.mType = FileType::Directory;
|
|
|
|
} else {
|
|
|
|
info.mType = FileType::Other;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int64_t size = -1;
|
|
|
|
if (info.mType == FileType::Regular) {
|
|
|
|
MOZ_TRY(file->GetFileSize(&size));
|
|
|
|
}
|
|
|
|
info.mSize = size;
|
|
|
|
PRTime lastModified = 0;
|
|
|
|
MOZ_TRY(file->GetLastModifiedTime(&lastModified));
|
|
|
|
info.mLastModified = static_cast<int64_t>(lastModified);
|
|
|
|
|
|
|
|
return info;
|
|
|
|
}
|
|
|
|
|
2020-08-26 18:31:57 +03:00
|
|
|
/* static */
|
|
|
|
Result<int64_t, IOUtils::IOError> IOUtils::TouchSync(
|
|
|
|
const nsAString& aPath, const Maybe<int64_t>& aNewModTime) {
|
|
|
|
MOZ_ASSERT(!NS_IsMainThread());
|
|
|
|
|
|
|
|
RefPtr<nsLocalFile> file = new nsLocalFile();
|
|
|
|
MOZ_TRY(file->InitWithPath(aPath));
|
|
|
|
|
|
|
|
int64_t now = aNewModTime.valueOrFrom([]() {
|
|
|
|
// NB: PR_Now reports time in microseconds since the Unix epoch
|
|
|
|
// (1970-01-01T00:00:00Z). Both nsLocalFile's lastModifiedTime and
|
|
|
|
// JavaScript's Date primitive values are to be expressed in
|
|
|
|
// milliseconds since Epoch.
|
|
|
|
int64_t nowMicros = PR_Now();
|
|
|
|
int64_t nowMillis = nowMicros / PR_USEC_PER_MSEC;
|
|
|
|
return nowMillis;
|
|
|
|
});
|
|
|
|
|
|
|
|
// nsIFile::SetLastModifiedTime will *not* do what is expected when passed 0
|
|
|
|
// as an argument. Rather than setting the time to 0, it will recalculate the
|
|
|
|
// system time and set it to that value instead. We explicit forbid this,
|
|
|
|
// because this side effect is surprising.
|
|
|
|
//
|
|
|
|
// If it ever becomes possible to set a file time to 0, this check should be
|
|
|
|
// removed, though this use case seems rare.
|
|
|
|
if (now == 0) {
|
|
|
|
return Err(
|
|
|
|
IOError(NS_ERROR_ILLEGAL_VALUE)
|
|
|
|
.WithMessage(
|
|
|
|
"Refusing to set the modification time of file(%s) to 0.\n"
|
|
|
|
"To use the current system time, call `touch` with no "
|
|
|
|
"arguments"));
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult rv = file->SetLastModifiedTime(now);
|
|
|
|
|
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
IOError err(rv);
|
|
|
|
if (IsFileNotFound(rv)) {
|
|
|
|
return Err(
|
|
|
|
err.WithMessage("Could not touch file(%s) because it does not exist",
|
|
|
|
NS_ConvertUTF16toUTF8(aPath).get()));
|
|
|
|
}
|
|
|
|
return Err(err);
|
|
|
|
}
|
|
|
|
return now;
|
|
|
|
}
|
|
|
|
|
2020-07-03 01:32:03 +03:00
|
|
|
NS_IMPL_ISUPPORTS(IOUtilsShutdownBlocker, nsIAsyncShutdownBlocker);
|
|
|
|
|
|
|
|
NS_IMETHODIMP IOUtilsShutdownBlocker::GetName(nsAString& aName) {
|
|
|
|
aName = u"IOUtils Blocker"_ns;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP IOUtilsShutdownBlocker::BlockShutdown(
|
|
|
|
nsIAsyncShutdownClient* aBarrierClient) {
|
|
|
|
nsCOMPtr<nsISerialEventTarget> et = IOUtils::GetBackgroundEventTarget();
|
|
|
|
|
|
|
|
IOUtils::sShutdownStarted = true;
|
|
|
|
|
|
|
|
if (!IOUtils::sBarrier) {
|
|
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsCOMPtr<nsIRunnable> backgroundRunnable =
|
|
|
|
NS_NewRunnableFunction(__func__, [self = RefPtr(this)]() {
|
|
|
|
nsCOMPtr<nsIRunnable> mainThreadRunnable =
|
|
|
|
NS_NewRunnableFunction(__func__, [self = RefPtr(self)]() {
|
|
|
|
IOUtils::sBarrier->RemoveBlocker(self);
|
|
|
|
|
|
|
|
auto lockedBackgroundET = IOUtils::sBackgroundEventTarget.Lock();
|
|
|
|
*lockedBackgroundET = nullptr;
|
|
|
|
IOUtils::sBarrier = nullptr;
|
|
|
|
});
|
2020-07-15 19:04:17 +03:00
|
|
|
nsresult rv = NS_DispatchToMainThread(mainThreadRunnable.forget());
|
|
|
|
NS_ENSURE_SUCCESS_VOID(rv);
|
2020-07-03 01:32:03 +03:00
|
|
|
});
|
|
|
|
|
|
|
|
return et->Dispatch(backgroundRunnable.forget(),
|
|
|
|
nsIEventTarget::DISPATCH_NORMAL);
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP IOUtilsShutdownBlocker::GetState(nsIPropertyBag** aState) {
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace dom
|
|
|
|
} // namespace mozilla
|
|
|
|
|
|
|
|
#undef REJECT_IF_NULL_EVENT_TARGET
|
|
|
|
#undef REJECT_IF_SHUTTING_DOWN
|
2020-07-30 17:02:32 +03:00
|
|
|
#undef REJECT_IF_RELATIVE_PATH
|