Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/xpcom/io/SpecialSystemDirectory.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 "SpecialSystemDirectory.h"
8
#include "nsString.h"
9
#include "nsDependentString.h"
10
#include "nsAutoPtr.h"
11
12
#if defined(XP_WIN)
13
14
#include <windows.h>
15
#include <stdlib.h>
16
#include <stdio.h>
17
#include <string.h>
18
#include <direct.h>
19
#include <shlobj.h>
20
#include <knownfolders.h>
21
#include <guiddef.h>
22
23
#elif defined(XP_UNIX)
24
25
#include <limits.h>
26
#include <unistd.h>
27
#include <stdlib.h>
28
#include <sys/param.h>
29
#include "prenv.h"
30
#if defined(MOZ_WIDGET_COCOA)
31
#include "CocoaFileUtils.h"
32
#endif
33
34
#endif
35
36
#ifndef MAXPATHLEN
37
#ifdef PATH_MAX
38
#define MAXPATHLEN PATH_MAX
39
#elif defined(MAX_PATH)
40
#define MAXPATHLEN MAX_PATH
41
#elif defined(_MAX_PATH)
42
#define MAXPATHLEN _MAX_PATH
43
#elif defined(CCHMAXPATH)
44
#define MAXPATHLEN CCHMAXPATH
45
#else
46
#define MAXPATHLEN 1024
47
#endif
48
#endif
49
50
#if defined (XP_WIN)
51
52
static nsresult
53
GetKnownFolder(GUID* aGuid, nsIFile** aFile)
54
{
55
  if (!aGuid) {
56
    return NS_ERROR_FAILURE;
57
  }
58
59
  PWSTR path = nullptr;
60
  SHGetKnownFolderPath(*aGuid, 0, nullptr, &path);
61
62
  if (!path) {
63
    return NS_ERROR_FAILURE;
64
  }
65
66
  nsresult rv = NS_NewLocalFile(nsDependentString(path),
67
                                true,
68
                                aFile);
69
70
  CoTaskMemFree(path);
71
  return rv;
72
}
73
74
static nsresult
75
GetWindowsFolder(int aFolder, nsIFile** aFile)
76
{
77
  WCHAR path_orig[MAX_PATH + 3];
78
  WCHAR* path = path_orig + 1;
79
  HRESULT result = SHGetSpecialFolderPathW(nullptr, path, aFolder, true);
80
81
  if (!SUCCEEDED(result)) {
82
    return NS_ERROR_FAILURE;
83
  }
84
85
  // Append the trailing slash
86
  int len = wcslen(path);
87
  if (len == 0) {
88
    return NS_ERROR_FILE_UNRECOGNIZED_PATH;
89
  }
90
  if (len > 1 && path[len - 1] != L'\\') {
91
    path[len]   = L'\\';
92
    path[++len] = L'\0';
93
  }
94
95
  return NS_NewLocalFile(nsDependentString(path, len), true, aFile);
96
}
97
98
#if WINVER < 0x0601
99
__inline HRESULT
100
SHLoadLibraryFromKnownFolder(REFKNOWNFOLDERID aFolderId, DWORD aMode,
101
                             REFIID riid, void** ppv)
102
{
103
  *ppv = nullptr;
104
  IShellLibrary* plib;
105
  HRESULT hr = CoCreateInstance(CLSID_ShellLibrary, nullptr,
106
                                CLSCTX_INPROC_SERVER,
107
                                IID_PPV_ARGS(&plib));
108
  if (SUCCEEDED(hr)) {
109
    hr = plib->LoadLibraryFromKnownFolder(aFolderId, aMode);
110
    if (SUCCEEDED(hr)) {
111
      hr = plib->QueryInterface(riid, ppv);
112
    }
113
    plib->Release();
114
  }
115
  return hr;
116
}
117
#endif
118
119
/*
120
 * Return the default save-to location for the Windows Library passed in
121
 * through aFolderId.
122
 */
123
static nsresult
124
GetLibrarySaveToPath(int aFallbackFolderId, REFKNOWNFOLDERID aFolderId,
125
                     nsIFile** aFile)
126
{
127
  RefPtr<IShellLibrary> shellLib;
128
  RefPtr<IShellItem> savePath;
129
  SHLoadLibraryFromKnownFolder(aFolderId, STGM_READ,
130
                               IID_IShellLibrary, getter_AddRefs(shellLib));
131
132
  if (shellLib &&
133
      SUCCEEDED(shellLib->GetDefaultSaveFolder(DSFT_DETECT, IID_IShellItem,
134
                                               getter_AddRefs(savePath)))) {
135
    wchar_t* str = nullptr;
136
    if (SUCCEEDED(savePath->GetDisplayName(SIGDN_FILESYSPATH, &str))) {
137
      nsAutoString path;
138
      path.Assign(str);
139
      path.Append('\\');
140
      nsresult rv =
141
        NS_NewLocalFile(path, false, aFile);
142
      CoTaskMemFree(str);
143
      return rv;
144
    }
145
  }
146
147
  return GetWindowsFolder(aFallbackFolderId, aFile);
148
}
149
150
/**
151
 * Provides a fallback for getting the path to APPDATA or LOCALAPPDATA by
152
 * querying the registry when the call to SHGetSpecialFolderPathW is unable to
153
 * provide these paths (Bug 513958).
154
 */
155
static nsresult
156
GetRegWindowsAppDataFolder(bool aLocal, nsIFile** aFile)
157
{
158
  HKEY key;
159
  LPCWSTR keyName =
160
    L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders";
161
  DWORD res = ::RegOpenKeyExW(HKEY_CURRENT_USER, keyName, 0, KEY_READ,
162
                              &key);
163
  if (res != ERROR_SUCCESS) {
164
    return NS_ERROR_FAILURE;
165
  }
166
167
  WCHAR path[MAX_PATH + 2];
168
  DWORD type, size;
169
  res = RegQueryValueExW(key, (aLocal ? L"Local AppData" : L"AppData"),
170
                         nullptr, &type, (LPBYTE)&path, &size);
171
  ::RegCloseKey(key);
172
  // The call to RegQueryValueExW must succeed, the type must be REG_SZ, the
173
  // buffer size must not equal 0, and the buffer size be a multiple of 2.
174
  if (res != ERROR_SUCCESS || type != REG_SZ || size == 0 || size % 2 != 0) {
175
    return NS_ERROR_FAILURE;
176
  }
177
178
  // Append the trailing slash
179
  int len = wcslen(path);
180
  if (len > 1 && path[len - 1] != L'\\') {
181
    path[len]   = L'\\';
182
    path[++len] = L'\0';
183
  }
184
185
  return NS_NewLocalFile(nsDependentString(path, len), true, aFile);
186
}
187
188
#endif // XP_WIN
189
190
#if defined(XP_UNIX)
191
static nsresult
192
GetUnixHomeDir(nsIFile** aFile)
193
3
{
194
#if defined(ANDROID)
195
  // XXX no home dir on android; maybe we should return the sdcard if present?
196
  return NS_ERROR_FAILURE;
197
#else
198
  return NS_NewNativeLocalFile(nsDependentCString(PR_GetEnv("HOME")),
199
3
                               true, aFile);
200
3
#endif
201
3
}
202
203
/*
204
  The following license applies to the xdg_user_dir_lookup function:
205
206
  Copyright (c) 2007 Red Hat, Inc.
207
208
  Permission is hereby granted, free of charge, to any person
209
  obtaining a copy of this software and associated documentation files
210
  (the "Software"), to deal in the Software without restriction,
211
  including without limitation the rights to use, copy, modify, merge,
212
  publish, distribute, sublicense, and/or sell copies of the Software,
213
  and to permit persons to whom the Software is furnished to do so,
214
  subject to the following conditions:
215
216
  The above copyright notice and this permission notice shall be
217
  included in all copies or substantial portions of the Software.
218
219
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
220
  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
221
  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
222
  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
223
  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
224
  ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
225
  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
226
  SOFTWARE.
227
*/
228
229
static char*
230
xdg_user_dir_lookup(const char* aType)
231
0
{
232
0
  FILE* file;
233
0
  char* home_dir;
234
0
  char* config_home;
235
0
  char* config_file;
236
0
  char buffer[512];
237
0
  char* user_dir;
238
0
  char* p;
239
0
  char* d;
240
0
  int len;
241
0
  int relative;
242
0
243
0
  home_dir = getenv("HOME");
244
0
245
0
  if (!home_dir) {
246
0
    goto error;
247
0
  }
248
0
249
0
  config_home = getenv("XDG_CONFIG_HOME");
250
0
  if (!config_home || config_home[0] == 0) {
251
0
    config_file = (char*)malloc(strlen(home_dir) +
252
0
                                strlen("/.config/user-dirs.dirs") + 1);
253
0
    if (!config_file) {
254
0
      goto error;
255
0
    }
256
0
257
0
    strcpy(config_file, home_dir);
258
0
    strcat(config_file, "/.config/user-dirs.dirs");
259
0
  } else {
260
0
    config_file = (char*)malloc(strlen(config_home) +
261
0
                                strlen("/user-dirs.dirs") + 1);
262
0
    if (!config_file) {
263
0
      goto error;
264
0
    }
265
0
266
0
    strcpy(config_file, config_home);
267
0
    strcat(config_file, "/user-dirs.dirs");
268
0
  }
269
0
270
0
  file = fopen(config_file, "r");
271
0
  free(config_file);
272
0
  if (!file) {
273
0
    goto error;
274
0
  }
275
0
276
0
  user_dir = nullptr;
277
0
  while (fgets(buffer, sizeof(buffer), file)) {
278
0
    /* Remove newline at end */
279
0
    len = strlen(buffer);
280
0
    if (len > 0 && buffer[len - 1] == '\n') {
281
0
      buffer[len - 1] = 0;
282
0
    }
283
0
284
0
    p = buffer;
285
0
    while (*p == ' ' || *p == '\t') {
286
0
      p++;
287
0
    }
288
0
289
0
    if (strncmp(p, "XDG_", 4) != 0) {
290
0
      continue;
291
0
    }
292
0
    p += 4;
293
0
    if (strncmp(p, aType, strlen(aType)) != 0) {
294
0
      continue;
295
0
    }
296
0
    p += strlen(aType);
297
0
    if (strncmp(p, "_DIR", 4) != 0) {
298
0
      continue;
299
0
    }
300
0
    p += 4;
301
0
302
0
    while (*p == ' ' || *p == '\t') {
303
0
      p++;
304
0
    }
305
0
306
0
    if (*p != '=') {
307
0
      continue;
308
0
    }
309
0
    p++;
310
0
311
0
    while (*p == ' ' || *p == '\t') {
312
0
      p++;
313
0
    }
314
0
315
0
    if (*p != '"') {
316
0
      continue;
317
0
    }
318
0
    p++;
319
0
320
0
    relative = 0;
321
0
    if (strncmp(p, "$HOME/", 6) == 0) {
322
0
      p += 6;
323
0
      relative = 1;
324
0
    } else if (*p != '/') {
325
0
      continue;
326
0
    }
327
0
328
0
    if (relative) {
329
0
      user_dir = (char*)malloc(strlen(home_dir) + 1 + strlen(p) + 1);
330
0
      if (!user_dir) {
331
0
        goto error2;
332
0
      }
333
0
334
0
      strcpy(user_dir, home_dir);
335
0
      strcat(user_dir, "/");
336
0
    } else {
337
0
      user_dir = (char*)malloc(strlen(p) + 1);
338
0
      if (!user_dir) {
339
0
        goto error2;
340
0
      }
341
0
342
0
      *user_dir = 0;
343
0
    }
344
0
345
0
    d = user_dir + strlen(user_dir);
346
0
    while (*p && *p != '"') {
347
0
      if ((*p == '\\') && (*(p + 1) != 0)) {
348
0
        p++;
349
0
      }
350
0
      *d++ = *p++;
351
0
    }
352
0
    *d = 0;
353
0
  }
354
0
error2:
355
0
  fclose(file);
356
0
357
0
  if (user_dir) {
358
0
    return user_dir;
359
0
  }
360
0
361
0
error:
362
0
  return nullptr;
363
0
}
364
365
static const char xdg_user_dirs[] =
366
  "DESKTOP\0"
367
  "DOCUMENTS\0"
368
  "DOWNLOAD\0"
369
  "MUSIC\0"
370
  "PICTURES\0"
371
  "PUBLICSHARE\0"
372
  "TEMPLATES\0"
373
  "VIDEOS";
374
375
static const uint8_t xdg_user_dir_offsets[] = {
376
  0,
377
  8,
378
  18,
379
  27,
380
  33,
381
  42,
382
  54,
383
  64
384
};
385
386
static nsresult
387
GetUnixXDGUserDirectory(SystemDirectories aSystemDirectory,
388
                        nsIFile** aFile)
389
0
{
390
0
  char* dir = xdg_user_dir_lookup(
391
0
    xdg_user_dirs + xdg_user_dir_offsets[aSystemDirectory - Unix_XDG_Desktop]);
392
0
393
0
  nsresult rv;
394
0
  nsCOMPtr<nsIFile> file;
395
0
  if (dir) {
396
0
    rv = NS_NewNativeLocalFile(nsDependentCString(dir), true,
397
0
                               getter_AddRefs(file));
398
0
    free(dir);
399
0
  } else if (Unix_XDG_Desktop == aSystemDirectory) {
400
0
    // for the XDG desktop dir, fall back to HOME/Desktop
401
0
    // (for historical compatibility)
402
0
    rv = GetUnixHomeDir(getter_AddRefs(file));
403
0
    if (NS_FAILED(rv)) {
404
0
      return rv;
405
0
    }
406
0
407
0
    rv = file->AppendNative(NS_LITERAL_CSTRING("Desktop"));
408
0
  } else {
409
0
    // no fallback for the other XDG dirs
410
0
    rv = NS_ERROR_FAILURE;
411
0
  }
412
0
413
0
  if (NS_FAILED(rv)) {
414
0
    return rv;
415
0
  }
416
0
417
0
  bool exists;
418
0
  rv = file->Exists(&exists);
419
0
  if (NS_FAILED(rv)) {
420
0
    return rv;
421
0
  }
422
0
  if (!exists) {
423
0
    rv = file->Create(nsIFile::DIRECTORY_TYPE, 0755);
424
0
    if (NS_FAILED(rv)) {
425
0
      return rv;
426
0
    }
427
0
  }
428
0
429
0
  *aFile = nullptr;
430
0
  file.swap(*aFile);
431
0
432
0
  return NS_OK;
433
0
}
434
#endif
435
436
nsresult
437
GetSpecialSystemDirectory(SystemDirectories aSystemSystemDirectory,
438
                          nsIFile** aFile)
439
3
{
440
#if defined(XP_WIN)
441
  WCHAR path[MAX_PATH];
442
#else
443
  char path[MAXPATHLEN];
444
3
#endif
445
3
446
3
  switch (aSystemSystemDirectory) {
447
3
    case OS_CurrentWorkingDirectory:
448
#if defined(XP_WIN)
449
      if (!_wgetcwd(path, MAX_PATH)) {
450
        return NS_ERROR_FAILURE;
451
      }
452
      return NS_NewLocalFile(nsDependentString(path),
453
                             true,
454
                             aFile);
455
#else
456
0
      if (!getcwd(path, MAXPATHLEN)) {
457
0
        return NS_ERROR_FAILURE;
458
0
      }
459
0
#endif
460
0
461
0
#if !defined(XP_WIN)
462
0
      return NS_NewNativeLocalFile(nsDependentCString(path),
463
0
                                   true,
464
0
                                   aFile);
465
0
#endif
466
0
467
0
    case OS_TemporaryDirectory:
468
#if defined (XP_WIN)
469
    {
470
      DWORD len = ::GetTempPathW(MAX_PATH, path);
471
      if (len == 0) {
472
        break;
473
      }
474
      return NS_NewLocalFile(nsDependentString(path, len),
475
                             true,
476
                             aFile);
477
    }
478
#elif defined(MOZ_WIDGET_COCOA)
479
    {
480
      return GetOSXFolderType(kUserDomain, kTemporaryFolderType, aFile);
481
    }
482
483
#elif defined(XP_UNIX)
484
    {
485
0
      static const char* tPath = nullptr;
486
0
      if (!tPath) {
487
0
        tPath = PR_GetEnv("TMPDIR");
488
0
        if (!tPath || !*tPath) {
489
0
          tPath = PR_GetEnv("TMP");
490
0
          if (!tPath || !*tPath) {
491
0
            tPath = PR_GetEnv("TEMP");
492
0
            if (!tPath || !*tPath) {
493
0
              tPath = "/tmp/";
494
0
            }
495
0
          }
496
0
        }
497
0
      }
498
0
      return NS_NewNativeLocalFile(nsDependentCString(tPath),
499
0
                                   true,
500
0
                                   aFile);
501
0
    }
502
#else
503
    break;
504
#endif
505
#if defined (XP_WIN)
506
    case Win_SystemDirectory: {
507
      int32_t len = ::GetSystemDirectoryW(path, MAX_PATH);
508
509
      // Need enough space to add the trailing backslash
510
      if (!len || len > MAX_PATH - 2) {
511
        break;
512
      }
513
      path[len]   = L'\\';
514
      path[++len] = L'\0';
515
516
      return NS_NewLocalFile(nsDependentString(path, len),
517
                             true,
518
                             aFile);
519
    }
520
521
    case Win_WindowsDirectory: {
522
      int32_t len = ::GetWindowsDirectoryW(path, MAX_PATH);
523
524
      // Need enough space to add the trailing backslash
525
      if (!len || len > MAX_PATH - 2) {
526
        break;
527
      }
528
529
      path[len]   = L'\\';
530
      path[++len] = L'\0';
531
532
      return NS_NewLocalFile(nsDependentString(path, len),
533
                             true,
534
                             aFile);
535
    }
536
537
    case Win_ProgramFiles: {
538
      return GetWindowsFolder(CSIDL_PROGRAM_FILES, aFile);
539
    }
540
541
    case Win_HomeDirectory: {
542
      nsresult rv = GetWindowsFolder(CSIDL_PROFILE, aFile);
543
      if (NS_SUCCEEDED(rv)) {
544
        return rv;
545
      }
546
547
      int32_t len;
548
      if ((len = ::GetEnvironmentVariableW(L"HOME", path, MAX_PATH)) > 0) {
549
        // Need enough space to add the trailing backslash
550
        if (len > MAX_PATH - 2) {
551
          break;
552
        }
553
554
        path[len]   = L'\\';
555
        path[++len] = L'\0';
556
557
        rv = NS_NewLocalFile(nsDependentString(path, len),
558
                             true,
559
                             aFile);
560
        if (NS_SUCCEEDED(rv)) {
561
          return rv;
562
        }
563
      }
564
565
      len = ::GetEnvironmentVariableW(L"HOMEDRIVE", path, MAX_PATH);
566
      if (0 < len && len < MAX_PATH) {
567
        WCHAR temp[MAX_PATH];
568
        DWORD len2 = ::GetEnvironmentVariableW(L"HOMEPATH", temp, MAX_PATH);
569
        if (0 < len2 && len + len2 < MAX_PATH) {
570
          wcsncat(path, temp, len2);
571
        }
572
573
        len = wcslen(path);
574
575
        // Need enough space to add the trailing backslash
576
        if (len > MAX_PATH - 2) {
577
          break;
578
        }
579
580
        path[len]   = L'\\';
581
        path[++len] = L'\0';
582
583
        return NS_NewLocalFile(nsDependentString(path, len),
584
                               true,
585
                               aFile);
586
      }
587
    }
588
    case Win_Programs: {
589
      return GetWindowsFolder(CSIDL_PROGRAMS, aFile);
590
    }
591
592
    case Win_Downloads: {
593
      // Defined in KnownFolders.h.
594
      GUID folderid_downloads = {
595
        0x374de290, 0x123f, 0x4565,
596
        { 0x91, 0x64, 0x39, 0xc4, 0x92, 0x5e, 0x46, 0x7b }
597
      };
598
      nsresult rv = GetKnownFolder(&folderid_downloads, aFile);
599
      // On WinXP, there is no downloads folder, default
600
      // to 'Desktop'.
601
      if (NS_ERROR_FAILURE == rv) {
602
        rv = GetWindowsFolder(CSIDL_DESKTOP, aFile);
603
      }
604
      return rv;
605
    }
606
607
    case Win_Favorites: {
608
      return GetWindowsFolder(CSIDL_FAVORITES, aFile);
609
    }
610
    case Win_Desktopdirectory: {
611
      return GetWindowsFolder(CSIDL_DESKTOPDIRECTORY, aFile);
612
    }
613
    case Win_Cookies: {
614
      return GetWindowsFolder(CSIDL_COOKIES, aFile);
615
    }
616
    case Win_Appdata: {
617
      nsresult rv = GetWindowsFolder(CSIDL_APPDATA, aFile);
618
      if (NS_FAILED(rv)) {
619
        rv = GetRegWindowsAppDataFolder(false, aFile);
620
      }
621
      return rv;
622
    }
623
    case Win_LocalAppdata: {
624
      nsresult rv = GetWindowsFolder(CSIDL_LOCAL_APPDATA, aFile);
625
      if (NS_FAILED(rv)) {
626
        rv = GetRegWindowsAppDataFolder(true, aFile);
627
      }
628
      return rv;
629
    }
630
#if defined(MOZ_CONTENT_SANDBOX)
631
    case Win_LocalAppdataLow: {
632
      GUID localAppDataLowGuid = FOLDERID_LocalAppDataLow;
633
      return GetKnownFolder(&localAppDataLowGuid, aFile);
634
    }
635
#endif
636
#if defined(MOZ_THUNDERBIRD) || defined(MOZ_SUITE)
637
    case Win_Documents: {
638
      return GetLibrarySaveToPath(CSIDL_MYDOCUMENTS,
639
                                  FOLDERID_DocumentsLibrary,
640
                                  aFile);
641
    }
642
#endif
643
#endif  // XP_WIN
644
645
0
#if defined(XP_UNIX)
646
3
    case Unix_HomeDirectory:
647
3
      return GetUnixHomeDir(aFile);
648
0
649
0
    case Unix_XDG_Desktop:
650
0
    case Unix_XDG_Download:
651
0
      return GetUnixXDGUserDirectory(aSystemSystemDirectory, aFile);
652
0
#endif
653
0
654
0
    default:
655
0
      break;
656
0
  }
657
0
  return NS_ERROR_NOT_AVAILABLE;
658
0
}
659
660
#if defined (MOZ_WIDGET_COCOA)
661
nsresult
662
GetOSXFolderType(short aDomain, OSType aFolderType, nsIFile** aLocalFile)
663
{
664
  nsresult rv = NS_ERROR_FAILURE;
665
666
  if (aFolderType == kTemporaryFolderType) {
667
    NS_NewLocalFile(EmptyString(), true, aLocalFile);
668
    nsCOMPtr<nsILocalFileMac> localMacFile(do_QueryInterface(*aLocalFile));
669
    if (localMacFile) {
670
      rv = localMacFile->InitWithCFURL(
671
             CocoaFileUtils::GetTemporaryFolderCFURLRef());
672
    }
673
    return rv;
674
  }
675
676
  OSErr err;
677
  FSRef fsRef;
678
  err = ::FSFindFolder(aDomain, aFolderType, kCreateFolder, &fsRef);
679
  if (err == noErr) {
680
    NS_NewLocalFile(EmptyString(), true, aLocalFile);
681
    nsCOMPtr<nsILocalFileMac> localMacFile(do_QueryInterface(*aLocalFile));
682
    if (localMacFile) {
683
      rv = localMacFile->InitWithFSRef(&fsRef);
684
    }
685
  }
686
  return rv;
687
}
688
#endif
689