Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/xpcom/glue/FileUtils.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 "mozilla/FileUtils.h"
8
9
#include <errno.h>
10
#include <stdio.h>
11
12
#include "nscore.h"
13
#include "private/pprio.h"
14
#include "prmem.h"
15
#include "mozilla/Assertions.h"
16
17
#if defined(XP_MACOSX)
18
#include <fcntl.h>
19
#include <unistd.h>
20
#include <mach/machine.h>
21
#include <mach-o/fat.h>
22
#include <mach-o/loader.h>
23
#include <sys/mman.h>
24
#include <sys/stat.h>
25
#include <limits.h>
26
#elif defined(XP_UNIX)
27
#include <fcntl.h>
28
#include <unistd.h>
29
#if defined(LINUX)
30
#include <elf.h>
31
#endif
32
#include <sys/types.h>
33
#include <sys/stat.h>
34
#elif defined(XP_WIN)
35
#include <windows.h>
36
#endif
37
38
// Functions that are not to be used in standalone glue must be implemented
39
// within this #if block
40
#if defined(MOZILLA_INTERNAL_API)
41
42
#include "nsString.h"
43
44
bool
45
mozilla::fallocate(PRFileDesc* aFD, int64_t aLength)
46
0
{
47
0
#if defined(HAVE_POSIX_FALLOCATE)
48
0
  return posix_fallocate(PR_FileDesc2NativeHandle(aFD), 0, aLength) == 0;
49
#elif defined(XP_WIN)
50
  int64_t oldpos = PR_Seek64(aFD, 0, PR_SEEK_CUR);
51
  if (oldpos == -1) {
52
    return false;
53
  }
54
55
  if (PR_Seek64(aFD, aLength, PR_SEEK_SET) != aLength) {
56
    return false;
57
  }
58
59
  bool retval = (0 != SetEndOfFile((HANDLE)PR_FileDesc2NativeHandle(aFD)));
60
61
  PR_Seek64(aFD, oldpos, PR_SEEK_SET);
62
  return retval;
63
#elif defined(XP_MACOSX)
64
  int fd = PR_FileDesc2NativeHandle(aFD);
65
  fstore_t store = {F_ALLOCATECONTIG, F_PEOFPOSMODE, 0, aLength};
66
  // Try to get a continous chunk of disk space
67
  int ret = fcntl(fd, F_PREALLOCATE, &store);
68
  if (ret == -1) {
69
    // OK, perhaps we are too fragmented, allocate non-continuous
70
    store.fst_flags = F_ALLOCATEALL;
71
    ret = fcntl(fd, F_PREALLOCATE, &store);
72
    if (ret == -1) {
73
      return false;
74
    }
75
  }
76
  return ftruncate(fd, aLength) == 0;
77
#elif defined(XP_UNIX)
78
  // The following is copied from fcntlSizeHint in sqlite
79
  /* If the OS does not have posix_fallocate(), fake it. First use
80
  ** ftruncate() to set the file size, then write a single byte to
81
  ** the last byte in each block within the extended region. This
82
  ** is the same technique used by glibc to implement posix_fallocate()
83
  ** on systems that do not have a real fallocate() system call.
84
  */
85
  int64_t oldpos = PR_Seek64(aFD, 0, PR_SEEK_CUR);
86
  if (oldpos == -1) {
87
    return false;
88
  }
89
90
  struct stat buf;
91
  int fd = PR_FileDesc2NativeHandle(aFD);
92
  if (fstat(fd, &buf)) {
93
    return false;
94
  }
95
96
  if (buf.st_size >= aLength) {
97
    return false;
98
  }
99
100
  const int nBlk = buf.st_blksize;
101
102
  if (!nBlk) {
103
    return false;
104
  }
105
106
  if (ftruncate(fd, aLength)) {
107
    return false;
108
  }
109
110
  int nWrite; // Return value from write()
111
  int64_t iWrite = ((buf.st_size + 2 * nBlk - 1) / nBlk) * nBlk - 1; // Next offset to write to
112
  while (iWrite < aLength) {
113
    nWrite = 0;
114
    if (PR_Seek64(aFD, iWrite, PR_SEEK_SET) == iWrite) {
115
      nWrite = PR_Write(aFD, "", 1);
116
    }
117
    if (nWrite != 1) {
118
      break;
119
    }
120
    iWrite += nBlk;
121
  }
122
123
  PR_Seek64(aFD, oldpos, PR_SEEK_SET);
124
  return nWrite == 1;
125
#endif
126
0
  return false;
127
0
}
128
129
void
130
mozilla::ReadAheadLib(nsIFile* aFile)
131
0
{
132
#if defined(XP_WIN)
133
  nsAutoString path;
134
  if (!aFile || NS_FAILED(aFile->GetPath(path))) {
135
    return;
136
  }
137
  ReadAheadLib(path.get());
138
#elif defined(LINUX) && !defined(ANDROID) || defined(XP_MACOSX)
139
  nsAutoCString nativePath;
140
0
  if (!aFile || NS_FAILED(aFile->GetNativePath(nativePath))) {
141
0
    return;
142
0
  }
143
0
  ReadAheadLib(nativePath.get());
144
0
#endif
145
0
}
146
147
void
148
mozilla::ReadAheadFile(nsIFile* aFile, const size_t aOffset,
149
                       const size_t aCount, mozilla::filedesc_t* aOutFd)
150
0
{
151
#if defined(XP_WIN)
152
  nsAutoString path;
153
  if (!aFile || NS_FAILED(aFile->GetPath(path))) {
154
    return;
155
  }
156
  ReadAheadFile(path.get(), aOffset, aCount, aOutFd);
157
#elif defined(LINUX) && !defined(ANDROID) || defined(XP_MACOSX)
158
  nsAutoCString nativePath;
159
0
  if (!aFile || NS_FAILED(aFile->GetNativePath(nativePath))) {
160
0
    return;
161
0
  }
162
0
  ReadAheadFile(nativePath.get(), aOffset, aCount, aOutFd);
163
0
#endif
164
0
}
165
166
mozilla::PathString
167
mozilla::GetLibraryName(mozilla::pathstr_t aDirectory, const char* aLib)
168
0
{
169
#ifdef XP_WIN
170
  nsAutoString fullName;
171
  if (aDirectory) {
172
    fullName.Assign(aDirectory);
173
    fullName.Append('\\');
174
  }
175
  AppendUTF8toUTF16(MakeStringSpan(aLib), fullName);
176
  if (!strstr(aLib, ".dll")) {
177
    fullName.AppendLiteral(".dll");
178
  }
179
  return std::move(fullName);
180
#else
181
  char* temp = PR_GetLibraryName(aDirectory, aLib);
182
0
  if (!temp) {
183
0
    return EmptyCString();
184
0
  }
185
0
  nsAutoCString libname(temp);
186
0
  PR_FreeLibraryName(temp);
187
0
  return std::move(libname);
188
0
#endif
189
0
}
190
191
mozilla::PathString
192
mozilla::GetLibraryFilePathname(mozilla::pathstr_t aName, PRFuncPtr aAddr)
193
0
{
194
#ifdef XP_WIN
195
  HMODULE handle = GetModuleHandleW(char16ptr_t(aName));
196
  if (!handle) {
197
    return EmptyString();
198
  }
199
200
  nsAutoString path;
201
  path.SetLength(MAX_PATH);
202
  DWORD len = GetModuleFileNameW(handle, char16ptr_t(path.BeginWriting()),
203
                                 path.Length());
204
  if (!len) {
205
    return EmptyString();
206
  }
207
208
  path.SetLength(len);
209
  return std::move(path);
210
#else
211
  char* temp = PR_GetLibraryFilePathname(aName, aAddr);
212
0
  if (!temp) {
213
0
    return EmptyCString();
214
0
  }
215
0
  nsAutoCString path(temp);
216
0
  PR_Free(temp); // PR_GetLibraryFilePathname() uses PR_Malloc().
217
0
  return std::move(path);
218
0
#endif
219
0
}
220
221
#endif // defined(MOZILLA_INTERNAL_API)
222
223
#if defined(LINUX) && !defined(ANDROID)
224
225
static const unsigned int bufsize = 4096;
226
227
#ifdef __LP64__
228
typedef Elf64_Ehdr Elf_Ehdr;
229
typedef Elf64_Phdr Elf_Phdr;
230
static const unsigned char ELFCLASS = ELFCLASS64;
231
typedef Elf64_Off Elf_Off;
232
#else
233
typedef Elf32_Ehdr Elf_Ehdr;
234
typedef Elf32_Phdr Elf_Phdr;
235
static const unsigned char ELFCLASS = ELFCLASS32;
236
typedef Elf32_Off Elf_Off;
237
#endif
238
239
#elif defined(XP_MACOSX)
240
241
#if defined(__i386__)
242
static const uint32_t CPU_TYPE = CPU_TYPE_X86;
243
#elif defined(__x86_64__)
244
static const uint32_t CPU_TYPE = CPU_TYPE_X86_64;
245
#elif defined(__ppc__)
246
static const uint32_t CPU_TYPE = CPU_TYPE_POWERPC;
247
#elif defined(__ppc64__)
248
static const uint32_t CPU_TYPE = CPU_TYPE_POWERPC64;
249
#else
250
#error Unsupported CPU type
251
#endif
252
253
#ifdef __LP64__
254
#undef LC_SEGMENT
255
#define LC_SEGMENT LC_SEGMENT_64
256
#undef MH_MAGIC
257
#define MH_MAGIC MH_MAGIC_64
258
#define cpu_mach_header mach_header_64
259
#define segment_command segment_command_64
260
#else
261
#define cpu_mach_header mach_header
262
#endif
263
264
class ScopedMMap
265
{
266
public:
267
  explicit ScopedMMap(const char* aFilePath)
268
    : buf(nullptr)
269
  {
270
    fd = open(aFilePath, O_RDONLY);
271
    if (fd < 0) {
272
      return;
273
    }
274
    struct stat st;
275
    if (fstat(fd, &st) < 0) {
276
      return;
277
    }
278
    size = st.st_size;
279
    buf = (char*)mmap(nullptr, size, PROT_READ, MAP_PRIVATE, fd, 0);
280
  }
281
  ~ScopedMMap()
282
  {
283
    if (buf) {
284
      munmap(buf, size);
285
    }
286
    if (fd >= 0) {
287
      close(fd);
288
    }
289
  }
290
  operator char*() { return buf; }
291
  int getFd() { return fd; }
292
private:
293
  int fd;
294
  char* buf;
295
  size_t size;
296
};
297
#endif
298
299
void
300
mozilla::ReadAhead(mozilla::filedesc_t aFd, const size_t aOffset,
301
                   const size_t aCount)
302
36
{
303
#if defined(XP_WIN)
304
305
  LARGE_INTEGER fpOriginal;
306
  LARGE_INTEGER fpOffset;
307
#if defined(HAVE_LONG_LONG)
308
  fpOffset.QuadPart = 0;
309
#else
310
  fpOffset.u.LowPart = 0;
311
  fpOffset.u.HighPart = 0;
312
#endif
313
314
  // Get the current file pointer so that we can restore it. This isn't
315
  // really necessary other than to provide the same semantics regarding the
316
  // file pointer that other platforms do
317
  if (!SetFilePointerEx(aFd, fpOffset, &fpOriginal, FILE_CURRENT)) {
318
    return;
319
  }
320
321
  if (aOffset) {
322
#if defined(HAVE_LONG_LONG)
323
    fpOffset.QuadPart = static_cast<LONGLONG>(aOffset);
324
#else
325
    fpOffset.u.LowPart = aOffset;
326
    fpOffset.u.HighPart = 0;
327
#endif
328
329
    if (!SetFilePointerEx(aFd, fpOffset, nullptr, FILE_BEGIN)) {
330
      return;
331
    }
332
  }
333
334
  char buf[64 * 1024];
335
  size_t totalBytesRead = 0;
336
  DWORD dwBytesRead;
337
  // Do dummy reads to trigger kernel-side readhead via FILE_FLAG_SEQUENTIAL_SCAN.
338
  // Abort when underfilling because during testing the buffers are read fully
339
  // A buffer that's not keeping up would imply that readahead isn't working right
340
  while (totalBytesRead < aCount &&
341
         ReadFile(aFd, buf, sizeof(buf), &dwBytesRead, nullptr) &&
342
         dwBytesRead == sizeof(buf)) {
343
    totalBytesRead += dwBytesRead;
344
  }
345
346
  // Restore the file pointer
347
  SetFilePointerEx(aFd, fpOriginal, nullptr, FILE_BEGIN);
348
349
#elif defined(LINUX) && !defined(ANDROID)
350
351
36
  readahead(aFd, aOffset, aCount);
352
36
353
#elif defined(XP_MACOSX)
354
355
  struct radvisory ra;
356
  ra.ra_offset = aOffset;
357
  ra.ra_count = aCount;
358
  // The F_RDADVISE fcntl is equivalent to Linux' readahead() system call.
359
  fcntl(aFd, F_RDADVISE, &ra);
360
361
#endif
362
}
363
364
void
365
mozilla::ReadAheadLib(mozilla::pathstr_t aFilePath)
366
36
{
367
36
  if (!aFilePath) {
368
0
    return;
369
0
  }
370
#if defined(XP_WIN)
371
  ReadAheadFile(aFilePath);
372
#elif defined(LINUX) && !defined(ANDROID)
373
36
  int fd = open(aFilePath, O_RDONLY);
374
36
  if (fd < 0) {
375
0
    return;
376
0
  }
377
36
378
36
  union
379
36
  {
380
36
    char buf[bufsize];
381
36
    Elf_Ehdr ehdr;
382
36
  } elf;
383
36
  // Read ELF header (ehdr) and program header table (phdr).
384
36
  // We check that the ELF magic is found, that the ELF class matches
385
36
  // our own, and that the program header table as defined in the ELF
386
36
  // headers fits in the buffer we read.
387
36
  if ((read(fd, elf.buf, bufsize) <= 0) ||
388
36
      (memcmp(elf.buf, ELFMAG, 4)) ||
389
36
      (elf.ehdr.e_ident[EI_CLASS] != ELFCLASS) ||
390
36
      // Upcast e_phentsize so the multiplication is done in the same precision
391
36
      // as the subsequent addition, to satisfy static analyzers and avoid
392
36
      // issues with abnormally large program header tables.
393
36
      (elf.ehdr.e_phoff + (static_cast<Elf_Off>(elf.ehdr.e_phentsize) *
394
36
                           elf.ehdr.e_phnum) >= bufsize)) {
395
0
    close(fd);
396
0
    return;
397
0
  }
398
36
  // The program header table contains segment definitions. One such
399
36
  // segment type is PT_LOAD, which describes how the dynamic loader
400
36
  // is going to map the file in memory. We use that information to
401
36
  // find the biggest offset from the library that will be mapped in
402
36
  // memory.
403
36
  Elf_Phdr* phdr = (Elf_Phdr*)&elf.buf[elf.ehdr.e_phoff];
404
36
  Elf_Off end = 0;
405
327
  for (int phnum = elf.ehdr.e_phnum; phnum; phdr++, phnum--) {
406
291
    if ((phdr->p_type == PT_LOAD) &&
407
291
        (end < phdr->p_offset + phdr->p_filesz)) {
408
72
      end = phdr->p_offset + phdr->p_filesz;
409
72
    }
410
291
  }
411
36
  // Let the kernel read ahead what the dynamic loader is going to
412
36
  // map in memory soon after.
413
36
  if (end > 0) {
414
36
    ReadAhead(fd, 0, end);
415
36
  }
416
36
  close(fd);
417
#elif defined(XP_MACOSX)
418
  ScopedMMap buf(aFilePath);
419
  char* base = buf;
420
  if (!base) {
421
    return;
422
  }
423
424
  // An OSX binary might either be a fat (universal) binary or a
425
  // Mach-O binary. A fat binary actually embeds several Mach-O
426
  // binaries. If we have a fat binary, find the offset where the
427
  // Mach-O binary for our CPU type can be found.
428
  struct fat_header* fh = (struct fat_header*)base;
429
430
  if (OSSwapBigToHostInt32(fh->magic) == FAT_MAGIC) {
431
    uint32_t nfat_arch = OSSwapBigToHostInt32(fh->nfat_arch);
432
    struct fat_arch* arch = (struct fat_arch*)&buf[sizeof(struct fat_header)];
433
    for (; nfat_arch; arch++, nfat_arch--) {
434
      if (OSSwapBigToHostInt32(arch->cputype) == CPU_TYPE) {
435
        base += OSSwapBigToHostInt32(arch->offset);
436
        break;
437
      }
438
    }
439
    if (base == buf) {
440
      return;
441
    }
442
  }
443
444
  // Check Mach-O magic in the Mach header
445
  struct cpu_mach_header* mh = (struct cpu_mach_header*)base;
446
  if (mh->magic != MH_MAGIC) {
447
    return;
448
  }
449
450
  // The Mach header is followed by a sequence of load commands.
451
  // Each command has a header containing the command type and the
452
  // command size. LD_SEGMENT commands describes how the dynamic
453
  // loader is going to map the file in memory. We use that
454
  // information to find the biggest offset from the library that
455
  // will be mapped in memory.
456
  char* cmd = &base[sizeof(struct cpu_mach_header)];
457
  uint32_t end = 0;
458
  for (uint32_t ncmds = mh->ncmds; ncmds; ncmds--) {
459
    struct segment_command* sh = (struct segment_command*)cmd;
460
    if (sh->cmd != LC_SEGMENT) {
461
      continue;
462
    }
463
    if (end < sh->fileoff + sh->filesize) {
464
      end = sh->fileoff + sh->filesize;
465
    }
466
    cmd += sh->cmdsize;
467
  }
468
  // Let the kernel read ahead what the dynamic loader is going to
469
  // map in memory soon after.
470
  if (end > 0) {
471
    ReadAhead(buf.getFd(), base - buf, end);
472
  }
473
#endif
474
}
475
476
void
477
mozilla::ReadAheadFile(mozilla::pathstr_t aFilePath, const size_t aOffset,
478
                       const size_t aCount, mozilla::filedesc_t* aOutFd)
479
0
{
480
#if defined(XP_WIN)
481
  if (!aFilePath) {
482
    if (aOutFd) {
483
      *aOutFd = INVALID_HANDLE_VALUE;
484
    }
485
    return;
486
  }
487
  HANDLE fd = CreateFileW(aFilePath, GENERIC_READ, FILE_SHARE_READ, nullptr,
488
                          OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, nullptr);
489
  if (aOutFd) {
490
    *aOutFd = fd;
491
  }
492
  if (fd == INVALID_HANDLE_VALUE) {
493
    return;
494
  }
495
  ReadAhead(fd, aOffset, aCount);
496
  if (!aOutFd) {
497
    CloseHandle(fd);
498
  }
499
#elif defined(LINUX) && !defined(ANDROID) || defined(XP_MACOSX)
500
0
  if (!aFilePath) {
501
0
    if (aOutFd) {
502
0
      *aOutFd = -1;
503
0
    }
504
0
    return;
505
0
  }
506
0
  int fd = open(aFilePath, O_RDONLY);
507
0
  if (aOutFd) {
508
0
    *aOutFd = fd;
509
0
  }
510
0
  if (fd < 0) {
511
0
    return;
512
0
  }
513
0
  size_t count;
514
0
  if (aCount == SIZE_MAX) {
515
0
    struct stat st;
516
0
    if (fstat(fd, &st) < 0) {
517
0
      if (!aOutFd) {
518
0
        close(fd);
519
0
      }
520
0
      return;
521
0
    }
522
0
    count = st.st_size;
523
0
  } else {
524
0
    count = aCount;
525
0
  }
526
0
  ReadAhead(fd, aOffset, count);
527
0
  if (!aOutFd) {
528
0
    close(fd);
529
0
  }
530
0
#endif
531
0
}
532