forked from mirrors/gecko-dev
There are ways for Create() to return access denied for files which already exist, particularly in the case of locked files. When it does, createUnique() should check whether the file exists before considering the attempt a failure. MozReview-Commit-ID: FyJTghk04jH --HG-- extra : rebase_source : 10e6f2cb7da18e8e6d410b52a593b7455f0d76fa
325 lines
8.1 KiB
C++
325 lines
8.1 KiB
C++
/* -*- 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/. */
|
|
|
|
#include "nsIServiceManager.h"
|
|
|
|
#include "nsLocalFile.h" // includes platform-specific headers
|
|
|
|
#include "nsString.h"
|
|
#include "nsCOMPtr.h"
|
|
#include "nsReadableUtils.h"
|
|
#include "nsPrintfCString.h"
|
|
#include "nsCRT.h"
|
|
#include "nsNativeCharsetUtils.h"
|
|
#include "nsUTF8Utils.h"
|
|
#include "nsArray.h"
|
|
|
|
#ifdef XP_WIN
|
|
#include <string.h>
|
|
#endif
|
|
|
|
|
|
#if !defined(MOZ_WIDGET_COCOA) && !defined(XP_WIN)
|
|
NS_IMETHODIMP
|
|
nsLocalFile::InitWithFile(nsIFile* aFile)
|
|
{
|
|
if (NS_WARN_IF(!aFile)) {
|
|
return NS_ERROR_INVALID_ARG;
|
|
}
|
|
|
|
nsAutoCString path;
|
|
aFile->GetNativePath(path);
|
|
if (path.IsEmpty()) {
|
|
return NS_ERROR_INVALID_ARG;
|
|
}
|
|
return InitWithNativePath(path);
|
|
}
|
|
#endif
|
|
|
|
#define kMaxFilenameLength 255
|
|
#define kMaxExtensionLength 100
|
|
#define kMaxSequenceNumberLength 5 // "-9999"
|
|
// requirement: kMaxExtensionLength < kMaxFilenameLength - kMaxSequenceNumberLength
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::CreateUnique(uint32_t aType, uint32_t aAttributes)
|
|
{
|
|
nsresult rv;
|
|
bool longName;
|
|
|
|
#ifdef XP_WIN
|
|
nsAutoString pathName, leafName, rootName, suffix;
|
|
rv = GetPath(pathName);
|
|
#else
|
|
nsAutoCString pathName, leafName, rootName, suffix;
|
|
rv = GetNativePath(pathName);
|
|
#endif
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
|
|
auto FailedBecauseExists = [&] (nsresult aRv) {
|
|
if (aRv == NS_ERROR_FILE_ACCESS_DENIED) {
|
|
bool exists;
|
|
return NS_SUCCEEDED(Exists(&exists)) && exists;
|
|
}
|
|
return aRv == NS_ERROR_FILE_ALREADY_EXISTS;
|
|
};
|
|
|
|
longName = (pathName.Length() + kMaxSequenceNumberLength >
|
|
kMaxFilenameLength);
|
|
if (!longName) {
|
|
rv = Create(aType, aAttributes);
|
|
if (!FailedBecauseExists(rv)) {
|
|
return rv;
|
|
}
|
|
}
|
|
|
|
#ifdef XP_WIN
|
|
rv = GetLeafName(leafName);
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
|
|
const int32_t lastDot = leafName.RFindChar(char16_t('.'));
|
|
#else
|
|
rv = GetNativeLeafName(leafName);
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
|
|
const int32_t lastDot = leafName.RFindChar('.');
|
|
#endif
|
|
|
|
if (lastDot == kNotFound) {
|
|
rootName = leafName;
|
|
} else {
|
|
suffix = Substring(leafName, lastDot); // include '.'
|
|
rootName = Substring(leafName, 0, lastDot); // strip suffix and dot
|
|
}
|
|
|
|
if (longName) {
|
|
int32_t maxRootLength = (kMaxFilenameLength -
|
|
(pathName.Length() - leafName.Length()) -
|
|
suffix.Length() - kMaxSequenceNumberLength);
|
|
|
|
// We cannot create an item inside a directory whose name is too long.
|
|
// Also, ensure that at least one character remains after we truncate
|
|
// the root name, as we don't want to end up with an empty leaf name.
|
|
if (maxRootLength < 2) {
|
|
return NS_ERROR_FILE_UNRECOGNIZED_PATH;
|
|
}
|
|
|
|
#ifdef XP_WIN
|
|
// ensure that we don't cut the name in mid-UTF16-character
|
|
rootName.SetLength(NS_IS_LOW_SURROGATE(rootName[maxRootLength]) ?
|
|
maxRootLength - 1 : maxRootLength);
|
|
SetLeafName(rootName + suffix);
|
|
#else
|
|
if (NS_IsNativeUTF8()) {
|
|
// ensure that we don't cut the name in mid-UTF8-character
|
|
// (assume the name is valid UTF8 to begin with)
|
|
while (UTF8traits::isInSeq(rootName[maxRootLength])) {
|
|
--maxRootLength;
|
|
}
|
|
|
|
// Another check to avoid ending up with an empty leaf name.
|
|
if (maxRootLength == 0 && suffix.IsEmpty()) {
|
|
return NS_ERROR_FILE_UNRECOGNIZED_PATH;
|
|
}
|
|
}
|
|
|
|
rootName.SetLength(maxRootLength);
|
|
SetNativeLeafName(rootName + suffix);
|
|
#endif
|
|
nsresult rvCreate = Create(aType, aAttributes);
|
|
if (!FailedBecauseExists(rvCreate)) {
|
|
return rvCreate;
|
|
}
|
|
}
|
|
|
|
for (int indx = 1; indx < 10000; ++indx) {
|
|
// start with "Picture-1.jpg" after "Picture.jpg" exists
|
|
#ifdef XP_WIN
|
|
SetLeafName(rootName +
|
|
NS_ConvertASCIItoUTF16(nsPrintfCString("-%d", indx)) +
|
|
suffix);
|
|
#else
|
|
SetNativeLeafName(rootName + nsPrintfCString("-%d", indx) + suffix);
|
|
#endif
|
|
rv = Create(aType, aAttributes);
|
|
if (NS_SUCCEEDED(rv) || !FailedBecauseExists(rv)) {
|
|
return rv;
|
|
}
|
|
}
|
|
|
|
// The disk is full, sort of
|
|
return NS_ERROR_FILE_TOO_BIG;
|
|
}
|
|
|
|
#if defined(XP_WIN)
|
|
static const char16_t kPathSeparatorChar = '\\';
|
|
#elif defined(XP_UNIX)
|
|
static const char16_t kPathSeparatorChar = '/';
|
|
#else
|
|
#error Need to define file path separator for your platform
|
|
#endif
|
|
|
|
static void
|
|
SplitPath(char16_t* aPath, nsTArray<char16_t*>& aNodeArray)
|
|
{
|
|
if (*aPath == 0) {
|
|
return;
|
|
}
|
|
|
|
if (*aPath == kPathSeparatorChar) {
|
|
aPath++;
|
|
}
|
|
aNodeArray.AppendElement(aPath);
|
|
|
|
for (char16_t* cp = aPath; *cp != 0; ++cp) {
|
|
if (*cp == kPathSeparatorChar) {
|
|
*cp++ = 0;
|
|
if (*cp == 0) {
|
|
break;
|
|
}
|
|
aNodeArray.AppendElement(cp);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::GetRelativeDescriptor(nsIFile* aFromFile, nsACString& aResult)
|
|
{
|
|
if (NS_WARN_IF(!aFromFile)) {
|
|
return NS_ERROR_INVALID_ARG;
|
|
}
|
|
|
|
//
|
|
// aResult will be UTF-8 encoded
|
|
//
|
|
|
|
nsresult rv;
|
|
aResult.Truncate(0);
|
|
|
|
nsAutoString thisPath, fromPath;
|
|
AutoTArray<char16_t*, 32> thisNodes;
|
|
AutoTArray<char16_t*, 32> fromNodes;
|
|
|
|
rv = GetPath(thisPath);
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
rv = aFromFile->GetPath(fromPath);
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
|
|
// get raw pointer to mutable string buffer
|
|
char16_t* thisPathPtr;
|
|
thisPath.BeginWriting(thisPathPtr);
|
|
char16_t* fromPathPtr;
|
|
fromPath.BeginWriting(fromPathPtr);
|
|
|
|
SplitPath(thisPathPtr, thisNodes);
|
|
SplitPath(fromPathPtr, fromNodes);
|
|
|
|
size_t nodeIndex;
|
|
for (nodeIndex = 0;
|
|
nodeIndex < thisNodes.Length() && nodeIndex < fromNodes.Length();
|
|
++nodeIndex) {
|
|
#ifdef XP_WIN
|
|
if (_wcsicmp(char16ptr_t(thisNodes[nodeIndex]),
|
|
char16ptr_t(fromNodes[nodeIndex]))) {
|
|
break;
|
|
}
|
|
#else
|
|
if (nsCRT::strcmp(thisNodes[nodeIndex], fromNodes[nodeIndex])) {
|
|
break;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
size_t branchIndex = nodeIndex;
|
|
for (nodeIndex = branchIndex; nodeIndex < fromNodes.Length(); ++nodeIndex) {
|
|
aResult.AppendLiteral("../");
|
|
}
|
|
for (nodeIndex = branchIndex; nodeIndex < thisNodes.Length(); ++nodeIndex) {
|
|
NS_ConvertUTF16toUTF8 nodeStr(thisNodes[nodeIndex]);
|
|
aResult.Append(nodeStr);
|
|
if (nodeIndex + 1 < thisNodes.Length()) {
|
|
aResult.Append('/');
|
|
}
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::SetRelativeDescriptor(nsIFile* aFromFile,
|
|
const nsACString& aRelativeDesc)
|
|
{
|
|
NS_NAMED_LITERAL_CSTRING(kParentDirStr, "../");
|
|
|
|
nsCOMPtr<nsIFile> targetFile;
|
|
nsresult rv = aFromFile->Clone(getter_AddRefs(targetFile));
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
|
|
//
|
|
// aRelativeDesc is UTF-8 encoded
|
|
//
|
|
|
|
nsCString::const_iterator strBegin, strEnd;
|
|
aRelativeDesc.BeginReading(strBegin);
|
|
aRelativeDesc.EndReading(strEnd);
|
|
|
|
nsCString::const_iterator nodeBegin(strBegin), nodeEnd(strEnd);
|
|
nsCString::const_iterator pos(strBegin);
|
|
|
|
nsCOMPtr<nsIFile> parentDir;
|
|
while (FindInReadable(kParentDirStr, nodeBegin, nodeEnd)) {
|
|
rv = targetFile->GetParent(getter_AddRefs(parentDir));
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
if (!parentDir) {
|
|
return NS_ERROR_FILE_UNRECOGNIZED_PATH;
|
|
}
|
|
targetFile = parentDir;
|
|
|
|
nodeBegin = nodeEnd;
|
|
pos = nodeEnd;
|
|
nodeEnd = strEnd;
|
|
}
|
|
|
|
nodeBegin = nodeEnd = pos;
|
|
while (nodeEnd != strEnd) {
|
|
FindCharInReadable('/', nodeEnd, strEnd);
|
|
targetFile->Append(NS_ConvertUTF8toUTF16(Substring(nodeBegin, nodeEnd)));
|
|
if (nodeEnd != strEnd) { // If there's more left in the string, inc over the '/' nodeEnd is on.
|
|
++nodeEnd;
|
|
}
|
|
nodeBegin = nodeEnd;
|
|
}
|
|
|
|
return InitWithFile(targetFile);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::GetRelativePath(nsIFile* aFromFile, nsACString& aResult)
|
|
{
|
|
return GetRelativeDescriptor(aFromFile, aResult);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::SetRelativePath(nsIFile* aFromFile,
|
|
const nsACString& aRelativePath)
|
|
{
|
|
return SetRelativeDescriptor(aFromFile, aRelativePath);
|
|
}
|