Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/xpcom/io/nsLocalFileCommon.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
 * License, v. 2.0. If a copy of the MPL was not distributed with this
5
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#include "nsIServiceManager.h"
8
9
#include "nsLocalFile.h" // includes platform-specific headers
10
11
#include "nsString.h"
12
#include "nsCOMPtr.h"
13
#include "nsReadableUtils.h"
14
#include "nsPrintfCString.h"
15
#include "nsCRT.h"
16
#include "nsNativeCharsetUtils.h"
17
#include "nsUTF8Utils.h"
18
#include "nsArray.h"
19
20
#ifdef XP_WIN
21
#include <string.h>
22
#endif
23
24
25
#if !defined(MOZ_WIDGET_COCOA) && !defined(XP_WIN)
26
NS_IMETHODIMP
27
nsLocalFile::InitWithFile(nsIFile* aFile)
28
0
{
29
0
  if (NS_WARN_IF(!aFile)) {
30
0
    return NS_ERROR_INVALID_ARG;
31
0
  }
32
0
33
0
  nsAutoCString path;
34
0
  aFile->GetNativePath(path);
35
0
  if (path.IsEmpty()) {
36
0
    return NS_ERROR_INVALID_ARG;
37
0
  }
38
0
  return InitWithNativePath(path);
39
0
}
40
#endif
41
42
3
#define kMaxFilenameLength 255
43
#define kMaxExtensionLength 100
44
3
#define kMaxSequenceNumberLength 5 // "-9999"
45
// requirement: kMaxExtensionLength < kMaxFilenameLength - kMaxSequenceNumberLength
46
47
NS_IMETHODIMP
48
nsLocalFile::CreateUnique(uint32_t aType, uint32_t aAttributes)
49
3
{
50
3
  nsresult rv;
51
3
  bool longName;
52
3
53
#ifdef XP_WIN
54
  nsAutoString pathName, leafName, rootName, suffix;
55
  rv = GetPath(pathName);
56
#else
57
  nsAutoCString pathName, leafName, rootName, suffix;
58
3
  rv = GetNativePath(pathName);
59
3
#endif
60
3
  if (NS_FAILED(rv)) {
61
0
    return rv;
62
0
  }
63
3
64
6
  auto FailedBecauseExists = [&] (nsresult aRv) {
65
6
    if (aRv == NS_ERROR_FILE_ACCESS_DENIED) {
66
0
      bool exists;
67
0
      return NS_SUCCEEDED(Exists(&exists)) && exists;
68
0
    }
69
6
    return aRv == NS_ERROR_FILE_ALREADY_EXISTS;
70
6
  };
71
3
72
3
  longName = (pathName.Length() + kMaxSequenceNumberLength >
73
3
              kMaxFilenameLength);
74
3
  if (!longName) {
75
3
    rv = Create(aType, aAttributes);
76
3
    if (!FailedBecauseExists(rv)) {
77
0
      return rv;
78
0
    }
79
3
  }
80
3
81
#ifdef XP_WIN
82
  rv = GetLeafName(leafName);
83
  if (NS_FAILED(rv)) {
84
    return rv;
85
  }
86
87
  const int32_t lastDot = leafName.RFindChar(char16_t('.'));
88
#else
89
3
  rv = GetNativeLeafName(leafName);
90
3
  if (NS_FAILED(rv)) {
91
0
    return rv;
92
0
  }
93
3
94
3
  const int32_t lastDot = leafName.RFindChar('.');
95
3
#endif
96
3
97
3
  if (lastDot == kNotFound) {
98
3
    rootName = leafName;
99
3
  } else {
100
0
    suffix = Substring(leafName, lastDot);      // include '.'
101
0
    rootName = Substring(leafName, 0, lastDot); // strip suffix and dot
102
0
  }
103
3
104
3
  if (longName) {
105
0
    int32_t maxRootLength = (kMaxFilenameLength -
106
0
                             (pathName.Length() - leafName.Length()) -
107
0
                             suffix.Length() - kMaxSequenceNumberLength);
108
0
109
0
    // We cannot create an item inside a directory whose name is too long.
110
0
    // Also, ensure that at least one character remains after we truncate
111
0
    // the root name, as we don't want to end up with an empty leaf name.
112
0
    if (maxRootLength < 2) {
113
0
      return NS_ERROR_FILE_UNRECOGNIZED_PATH;
114
0
    }
115
0
116
#ifdef XP_WIN
117
    // ensure that we don't cut the name in mid-UTF16-character
118
    rootName.SetLength(NS_IS_LOW_SURROGATE(rootName[maxRootLength]) ?
119
                       maxRootLength - 1 : maxRootLength);
120
    SetLeafName(rootName + suffix);
121
#else
122
0
    if (NS_IsNativeUTF8()) {
123
0
      // ensure that we don't cut the name in mid-UTF8-character
124
0
      // (assume the name is valid UTF8 to begin with)
125
0
      while (UTF8traits::isInSeq(rootName[maxRootLength])) {
126
0
        --maxRootLength;
127
0
      }
128
0
129
0
      // Another check to avoid ending up with an empty leaf name.
130
0
      if (maxRootLength == 0 && suffix.IsEmpty()) {
131
0
        return NS_ERROR_FILE_UNRECOGNIZED_PATH;
132
0
      }
133
0
    }
134
0
135
0
    rootName.SetLength(maxRootLength);
136
0
    SetNativeLeafName(rootName + suffix);
137
0
#endif
138
0
    nsresult rvCreate = Create(aType, aAttributes);
139
0
    if (!FailedBecauseExists(rvCreate)) {
140
0
      return rvCreate;
141
0
    }
142
3
  }
143
3
144
6
  for (int indx = 1; indx < 10000; ++indx) {
145
6
    // start with "Picture-1.jpg" after "Picture.jpg" exists
146
#ifdef XP_WIN
147
    SetLeafName(rootName +
148
                NS_ConvertASCIItoUTF16(nsPrintfCString("-%d", indx)) +
149
                suffix);
150
#else
151
    SetNativeLeafName(rootName + nsPrintfCString("-%d", indx) + suffix);
152
6
#endif
153
6
    rv = Create(aType, aAttributes);
154
6
    if (NS_SUCCEEDED(rv) || !FailedBecauseExists(rv)) {
155
3
      return rv;
156
3
    }
157
6
  }
158
3
159
3
  // The disk is full, sort of
160
3
  return NS_ERROR_FILE_TOO_BIG;
161
3
}
162
163
#if defined(XP_WIN)
164
static const char16_t kPathSeparatorChar       = '\\';
165
#elif defined(XP_UNIX)
166
static const char16_t kPathSeparatorChar       = '/';
167
#else
168
#error Need to define file path separator for your platform
169
#endif
170
171
static void
172
SplitPath(char16_t* aPath, nsTArray<char16_t*>& aNodeArray)
173
0
{
174
0
  if (*aPath == 0) {
175
0
    return;
176
0
  }
177
0
178
0
  if (*aPath == kPathSeparatorChar) {
179
0
    aPath++;
180
0
  }
181
0
  aNodeArray.AppendElement(aPath);
182
0
183
0
  for (char16_t* cp = aPath; *cp != 0; ++cp) {
184
0
    if (*cp == kPathSeparatorChar) {
185
0
      *cp++ = 0;
186
0
      if (*cp == 0) {
187
0
        break;
188
0
      }
189
0
      aNodeArray.AppendElement(cp);
190
0
    }
191
0
  }
192
0
}
193
194
195
NS_IMETHODIMP
196
nsLocalFile::GetRelativeDescriptor(nsIFile* aFromFile, nsACString& aResult)
197
0
{
198
0
  if (NS_WARN_IF(!aFromFile)) {
199
0
    return NS_ERROR_INVALID_ARG;
200
0
  }
201
0
202
0
  //
203
0
  // aResult will be UTF-8 encoded
204
0
  //
205
0
206
0
  nsresult rv;
207
0
  aResult.Truncate(0);
208
0
209
0
  nsAutoString thisPath, fromPath;
210
0
  AutoTArray<char16_t*, 32> thisNodes;
211
0
  AutoTArray<char16_t*, 32> fromNodes;
212
0
213
0
  rv = GetPath(thisPath);
214
0
  if (NS_FAILED(rv)) {
215
0
    return rv;
216
0
  }
217
0
  rv = aFromFile->GetPath(fromPath);
218
0
  if (NS_FAILED(rv)) {
219
0
    return rv;
220
0
  }
221
0
222
0
  // get raw pointer to mutable string buffer
223
0
  char16_t* thisPathPtr = thisPath.BeginWriting();
224
0
  char16_t* fromPathPtr = fromPath.BeginWriting();
225
0
226
0
  SplitPath(thisPathPtr, thisNodes);
227
0
  SplitPath(fromPathPtr, fromNodes);
228
0
229
0
  size_t nodeIndex;
230
0
  for (nodeIndex = 0;
231
0
       nodeIndex < thisNodes.Length() && nodeIndex < fromNodes.Length();
232
0
       ++nodeIndex) {
233
#ifdef XP_WIN
234
    if (_wcsicmp(char16ptr_t(thisNodes[nodeIndex]),
235
                 char16ptr_t(fromNodes[nodeIndex]))) {
236
      break;
237
    }
238
#else
239
0
    if (nsCRT::strcmp(thisNodes[nodeIndex], fromNodes[nodeIndex])) {
240
0
      break;
241
0
    }
242
0
#endif
243
0
  }
244
0
245
0
  size_t branchIndex = nodeIndex;
246
0
  for (nodeIndex = branchIndex; nodeIndex < fromNodes.Length(); ++nodeIndex) {
247
0
    aResult.AppendLiteral("../");
248
0
  }
249
0
  for (nodeIndex = branchIndex; nodeIndex < thisNodes.Length(); ++nodeIndex) {
250
0
    NS_ConvertUTF16toUTF8 nodeStr(thisNodes[nodeIndex]);
251
0
    aResult.Append(nodeStr);
252
0
    if (nodeIndex + 1 < thisNodes.Length()) {
253
0
      aResult.Append('/');
254
0
    }
255
0
  }
256
0
257
0
  return NS_OK;
258
0
}
259
260
NS_IMETHODIMP
261
nsLocalFile::SetRelativeDescriptor(nsIFile* aFromFile,
262
                                   const nsACString& aRelativeDesc)
263
0
{
264
0
  NS_NAMED_LITERAL_CSTRING(kParentDirStr, "../");
265
0
266
0
  nsCOMPtr<nsIFile> targetFile;
267
0
  nsresult rv = aFromFile->Clone(getter_AddRefs(targetFile));
268
0
  if (NS_FAILED(rv)) {
269
0
    return rv;
270
0
  }
271
0
272
0
  //
273
0
  // aRelativeDesc is UTF-8 encoded
274
0
  //
275
0
276
0
  nsCString::const_iterator strBegin, strEnd;
277
0
  aRelativeDesc.BeginReading(strBegin);
278
0
  aRelativeDesc.EndReading(strEnd);
279
0
280
0
  nsCString::const_iterator nodeBegin(strBegin), nodeEnd(strEnd);
281
0
  nsCString::const_iterator pos(strBegin);
282
0
283
0
  nsCOMPtr<nsIFile> parentDir;
284
0
  while (FindInReadable(kParentDirStr, nodeBegin, nodeEnd)) {
285
0
    rv = targetFile->GetParent(getter_AddRefs(parentDir));
286
0
    if (NS_FAILED(rv)) {
287
0
      return rv;
288
0
    }
289
0
    if (!parentDir) {
290
0
      return NS_ERROR_FILE_UNRECOGNIZED_PATH;
291
0
    }
292
0
    targetFile = parentDir;
293
0
294
0
    nodeBegin = nodeEnd;
295
0
    pos = nodeEnd;
296
0
    nodeEnd = strEnd;
297
0
  }
298
0
299
0
  nodeBegin = nodeEnd = pos;
300
0
  while (nodeEnd != strEnd) {
301
0
    FindCharInReadable('/', nodeEnd, strEnd);
302
0
    targetFile->Append(NS_ConvertUTF8toUTF16(Substring(nodeBegin, nodeEnd)));
303
0
    if (nodeEnd != strEnd) { // If there's more left in the string, inc over the '/' nodeEnd is on.
304
0
      ++nodeEnd;
305
0
    }
306
0
    nodeBegin = nodeEnd;
307
0
  }
308
0
309
0
  return InitWithFile(targetFile);
310
0
}
311
312
NS_IMETHODIMP
313
nsLocalFile::GetRelativePath(nsIFile* aFromFile, nsACString& aResult)
314
0
{
315
0
  return GetRelativeDescriptor(aFromFile, aResult);
316
0
}
317
318
NS_IMETHODIMP
319
nsLocalFile::SetRelativePath(nsIFile* aFromFile,
320
                             const nsACString& aRelativePath)
321
0
{
322
0
  return SetRelativeDescriptor(aFromFile, aRelativePath);
323
0
}