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 file,
5  * You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "mozilla/DebugOnly.h"
8 
9 #include "fcntl.h"
10 #include "errno.h"
11 
12 #include "prsystem.h"
13 
14 // Short macro to get the size of a member of a
15 // given struct at compile time.
16 // t is the type of struct, m the name of the
17 // member:
18 // DOM_SIZEOF_MEMBER(struct mystruct, myint)
19 // will give you the size of the type of myint.
20 #define DOM_SIZEOF_MEMBER(t, m) sizeof(((t*)0)->m)
21 
22 #if defined(XP_UNIX)
23 #  include "unistd.h"
24 #  include "dirent.h"
25 #  include "poll.h"
26 #  include "sys/stat.h"
27 #  if defined(XP_LINUX)
28 #    include <sys/vfs.h>
29 #    define statvfs statfs
30 #    define f_frsize f_bsize
31 #  else
32 #    include "sys/statvfs.h"
33 #  endif  // defined(XP_LINUX)
34 #  if !defined(ANDROID)
35 #    include "sys/wait.h"
36 #    include <spawn.h>
37 #  endif  // !defined(ANDROID)
38 #endif    // defined(XP_UNIX)
39 
40 #if defined(XP_LINUX)
41 #  include <linux/fadvise.h>
42 #endif  // defined(XP_LINUX)
43 
44 #if defined(XP_MACOSX)
45 #  include "copyfile.h"
46 #endif  // defined(XP_MACOSX)
47 
48 #if defined(XP_WIN)
49 #  include <windows.h>
50 #  include <accctrl.h>
51 
52 #  ifndef PATH_MAX
53 #    define PATH_MAX MAX_PATH
54 #  endif
55 
56 #endif  // defined(XP_WIN)
57 
58 #include "jsapi.h"
59 #include "jsfriendapi.h"
60 #include "BindingUtils.h"
61 
62 // Used to provide information on the OS
63 
64 #include "nsThreadUtils.h"
65 #include "nsIObserverService.h"
66 #include "nsIObserver.h"
67 #include "nsDirectoryServiceUtils.h"
68 #include "nsIXULRuntime.h"
69 #include "nsXPCOMCIDInternal.h"
70 #include "nsServiceManagerUtils.h"
71 #include "nsString.h"
72 #include "nsSystemInfo.h"
73 #include "nsDirectoryServiceDefs.h"
74 #include "nsXULAppAPI.h"
75 #include "nsAppDirectoryServiceDefs.h"
76 #include "mozJSComponentLoader.h"
77 
78 #include "mozilla/ClearOnShutdown.h"
79 #include "mozilla/StaticPtr.h"
80 #include "mozilla/UniquePtr.h"
81 
82 #include "OSFileConstants.h"
83 #include "nsZipArchive.h"
84 
85 #if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || \
86     defined(__OpenBSD__)
87 #  define __dd_fd dd_fd
88 #endif
89 
90 /**
91  * This module defines the basic libc constants (error numbers, open modes,
92  * etc.) used by OS.File and possibly other OS-bound JavaScript libraries.
93  */
94 
95 namespace mozilla {
96 
97 namespace {
98 
99 StaticRefPtr<OSFileConstantsService> gInstance;
100 
101 }  // anonymous namespace
102 
103 struct OSFileConstantsService::Paths {
104   /**
105    * The name of the directory holding all the libraries (libxpcom, libnss,
106    * etc.)
107    */
108   nsString libDir;
109   nsString tmpDir;
110   nsString profileDir;
111   nsString localProfileDir;
112 
Pathsmozilla::OSFileConstantsService::Paths113   Paths() {
114     libDir.SetIsVoid(true);
115     tmpDir.SetIsVoid(true);
116     profileDir.SetIsVoid(true);
117     localProfileDir.SetIsVoid(true);
118   }
119 };
120 
121 /**
122  * Return the path to one of the special directories.
123  *
124  * @param aKey The key to the special directory (e.g. "TmpD", "ProfD", ...)
125  * @param aOutPath The path to the special directory. In case of error,
126  * the string is set to void.
127  */
GetPathToSpecialDir(const char * aKey,nsString & aOutPath)128 nsresult GetPathToSpecialDir(const char* aKey, nsString& aOutPath) {
129   nsCOMPtr<nsIFile> file;
130   nsresult rv = NS_GetSpecialDirectory(aKey, getter_AddRefs(file));
131   if (NS_FAILED(rv) || !file) {
132     return rv;
133   }
134 
135   return file->GetPath(aOutPath);
136 }
137 
138 /**
139  * In some cases, OSFileConstants may be instantiated before the
140  * profile is setup. In such cases, |OS.Constants.Path.profileDir| and
141  * |OS.Constants.Path.localProfileDir| are undefined. However, we want
142  * to ensure that this does not break existing code, so that future
143  * workers spawned after the profile is setup have these constants.
144  *
145  * For this purpose, we register an observer to set |mPaths->profileDir|
146  * and |mPaths->localProfileDir| once the profile is setup.
147  */
148 NS_IMETHODIMP
Observe(nsISupports *,const char * aTopic,const char16_t *)149 OSFileConstantsService::Observe(nsISupports*, const char* aTopic,
150                                 const char16_t*) {
151   if (!mInitialized) {
152     // Initialization has not taken place, something is wrong,
153     // don't make things worse.
154     return NS_OK;
155   }
156 
157   nsresult rv =
158       GetPathToSpecialDir(NS_APP_USER_PROFILE_50_DIR, mPaths->profileDir);
159   if (NS_FAILED(rv)) {
160     return rv;
161   }
162   rv = GetPathToSpecialDir(NS_APP_USER_PROFILE_LOCAL_50_DIR,
163                            mPaths->localProfileDir);
164   if (NS_FAILED(rv)) {
165     return rv;
166   }
167 
168   return NS_OK;
169 }
170 
171 /**
172  * Perform the part of initialization that can only be
173  * executed on the main thread.
174  */
InitOSFileConstants()175 nsresult OSFileConstantsService::InitOSFileConstants() {
176   MOZ_ASSERT(NS_IsMainThread());
177   if (mInitialized) {
178     return NS_OK;
179   }
180 
181   UniquePtr<Paths> paths(new Paths);
182 
183   // Initialize paths->libDir
184   nsCOMPtr<nsIFile> file;
185   nsresult rv =
186       NS_GetSpecialDirectory(NS_XPCOM_LIBRARY_FILE, getter_AddRefs(file));
187   if (NS_FAILED(rv)) {
188     return rv;
189   }
190 
191   nsCOMPtr<nsIFile> libDir;
192   rv = file->GetParent(getter_AddRefs(libDir));
193   if (NS_FAILED(rv)) {
194     return rv;
195   }
196 
197   rv = libDir->GetPath(paths->libDir);
198   if (NS_FAILED(rv)) {
199     return rv;
200   }
201 
202   // Setup profileDir and localProfileDir immediately if possible (we
203   // assume that NS_APP_USER_PROFILE_50_DIR and
204   // NS_APP_USER_PROFILE_LOCAL_50_DIR are set simultaneously)
205   rv = GetPathToSpecialDir(NS_APP_USER_PROFILE_50_DIR, paths->profileDir);
206   if (NS_SUCCEEDED(rv)) {
207     rv = GetPathToSpecialDir(NS_APP_USER_PROFILE_LOCAL_50_DIR,
208                              paths->localProfileDir);
209   }
210 
211   // Otherwise, delay setup of profileDir/localProfileDir until they
212   // become available.
213   if (NS_FAILED(rv)) {
214     nsCOMPtr<nsIObserverService> obsService =
215         do_GetService(NS_OBSERVERSERVICE_CONTRACTID, &rv);
216     if (NS_FAILED(rv)) {
217       return rv;
218     }
219     rv = obsService->AddObserver(this, "profile-do-change", false);
220     if (NS_FAILED(rv)) {
221       return rv;
222     }
223   }
224 
225   GetPathToSpecialDir(NS_OS_TEMP_DIR, paths->tmpDir);
226 
227   mPaths = std::move(paths);
228 
229   // Get the umask from the system-info service.
230   // The property will always be present, but it will be zero on
231   // non-Unix systems.
232   // nsSystemInfo::gUserUmask is initialized by NS_InitXPCOM so we don't need
233   // to initialize the service.
234   mUserUmask = nsSystemInfo::gUserUmask;
235 
236   mInitialized = true;
237   return NS_OK;
238 }
239 
240 /**
241  * Define a simple read-only property holding an integer.
242  *
243  * @param name The name of the constant. Used both as the JS name for the
244  * constant and to access its value. Must be defined.
245  *
246  * Produces a |ConstantSpec|.
247  */
248 #define INT_CONSTANT(name) \
249   { #name, JS::Int32Value(name) }
250 
251 /**
252  * Define a simple read-only property holding an unsigned integer.
253  *
254  * @param name The name of the constant. Used both as the JS name for the
255  * constant and to access its value. Must be defined.
256  *
257  * Produces a |ConstantSpec|.
258  */
259 #define UINT_CONSTANT(name) \
260   { #name, JS::NumberValue(name) }
261 
262 /**
263  * End marker for ConstantSpec
264  */
265 #define PROP_END \
266   { nullptr, JS::UndefinedValue() }
267 
268 // Define missing constants for Android
269 #if !defined(S_IRGRP)
270 #  define S_IXOTH 0001
271 #  define S_IWOTH 0002
272 #  define S_IROTH 0004
273 #  define S_IRWXO 0007
274 #  define S_IXGRP 0010
275 #  define S_IWGRP 0020
276 #  define S_IRGRP 0040
277 #  define S_IRWXG 0070
278 #  define S_IXUSR 0100
279 #  define S_IWUSR 0200
280 #  define S_IRUSR 0400
281 #  define S_IRWXU 0700
282 #endif  // !defined(S_IRGRP)
283 
284 /**
285  * The properties defined in libc.
286  *
287  * If you extend this list of properties, please
288  * separate categories ("errors", "open", etc.),
289  * keep properties organized by alphabetical order
290  * and #ifdef-away properties that are not portable.
291  */
292 static const dom::ConstantSpec gLibcProperties[] = {
293     // Arguments for open
294     INT_CONSTANT(O_APPEND),
295 #if defined(O_CLOEXEC)
296     INT_CONSTANT(O_CLOEXEC),
297 #endif  // defined(O_CLOEXEC)
298     INT_CONSTANT(O_CREAT),
299 #if defined(O_DIRECTORY)
300     INT_CONSTANT(O_DIRECTORY),
301 #endif  // defined(O_DIRECTORY)
302 #if defined(O_EVTONLY)
303     INT_CONSTANT(O_EVTONLY),
304 #endif  // defined(O_EVTONLY)
305     INT_CONSTANT(O_EXCL),
306 #if defined(O_EXLOCK)
307     INT_CONSTANT(O_EXLOCK),
308 #endif  // defined(O_EXLOCK)
309 #if defined(O_LARGEFILE)
310     INT_CONSTANT(O_LARGEFILE),
311 #endif  // defined(O_LARGEFILE)
312 #if defined(O_NOFOLLOW)
313     INT_CONSTANT(O_NOFOLLOW),
314 #endif  // defined(O_NOFOLLOW)
315 #if defined(O_NONBLOCK)
316     INT_CONSTANT(O_NONBLOCK),
317 #endif  // defined(O_NONBLOCK)
318     INT_CONSTANT(O_RDONLY),
319     INT_CONSTANT(O_RDWR),
320 #if defined(O_RSYNC)
321     INT_CONSTANT(O_RSYNC),
322 #endif  // defined(O_RSYNC)
323 #if defined(O_SHLOCK)
324     INT_CONSTANT(O_SHLOCK),
325 #endif  // defined(O_SHLOCK)
326 #if defined(O_SYMLINK)
327     INT_CONSTANT(O_SYMLINK),
328 #endif  // defined(O_SYMLINK)
329 #if defined(O_SYNC)
330     INT_CONSTANT(O_SYNC),
331 #endif  // defined(O_SYNC)
332     INT_CONSTANT(O_TRUNC),
333     INT_CONSTANT(O_WRONLY),
334 
335 #if defined(FD_CLOEXEC)
336     INT_CONSTANT(FD_CLOEXEC),
337 #endif  // defined(FD_CLOEXEC)
338 
339 #if defined(AT_EACCESS)
340     INT_CONSTANT(AT_EACCESS),
341 #endif  // defined(AT_EACCESS)
342 #if defined(AT_FDCWD)
343     INT_CONSTANT(AT_FDCWD),
344 #endif  // defined(AT_FDCWD)
345 #if defined(AT_SYMLINK_NOFOLLOW)
346     INT_CONSTANT(AT_SYMLINK_NOFOLLOW),
347 #endif  // defined(AT_SYMLINK_NOFOLLOW)
348 
349 #if defined(POSIX_FADV_SEQUENTIAL)
350     INT_CONSTANT(POSIX_FADV_SEQUENTIAL),
351 #endif  // defined(POSIX_FADV_SEQUENTIAL)
352 
353 // access
354 #if defined(F_OK)
355     INT_CONSTANT(F_OK),
356     INT_CONSTANT(R_OK),
357     INT_CONSTANT(W_OK),
358     INT_CONSTANT(X_OK),
359 #endif  // defined(F_OK)
360 
361     // modes
362     INT_CONSTANT(S_IRGRP),
363     INT_CONSTANT(S_IROTH),
364     INT_CONSTANT(S_IRUSR),
365     INT_CONSTANT(S_IRWXG),
366     INT_CONSTANT(S_IRWXO),
367     INT_CONSTANT(S_IRWXU),
368     INT_CONSTANT(S_IWGRP),
369     INT_CONSTANT(S_IWOTH),
370     INT_CONSTANT(S_IWUSR),
371     INT_CONSTANT(S_IXOTH),
372     INT_CONSTANT(S_IXGRP),
373     INT_CONSTANT(S_IXUSR),
374 
375     // seek
376     INT_CONSTANT(SEEK_CUR),
377     INT_CONSTANT(SEEK_END),
378     INT_CONSTANT(SEEK_SET),
379 
380 #if defined(XP_UNIX)
381     // poll
382     INT_CONSTANT(POLLERR),
383     INT_CONSTANT(POLLHUP),
384     INT_CONSTANT(POLLIN),
385     INT_CONSTANT(POLLNVAL),
386     INT_CONSTANT(POLLOUT),
387 
388 // wait
389 #  if defined(WNOHANG)
390     INT_CONSTANT(WNOHANG),
391 #  endif  // defined(WNOHANG)
392 
393     // fcntl command values
394     INT_CONSTANT(F_GETLK),
395     INT_CONSTANT(F_SETFD),
396     INT_CONSTANT(F_SETFL),
397     INT_CONSTANT(F_SETLK),
398     INT_CONSTANT(F_SETLKW),
399 
400     // flock type values
401     INT_CONSTANT(F_RDLCK),
402     INT_CONSTANT(F_WRLCK),
403     INT_CONSTANT(F_UNLCK),
404 
405 // splice
406 #  if defined(SPLICE_F_MOVE)
407     INT_CONSTANT(SPLICE_F_MOVE),
408 #  endif  // defined(SPLICE_F_MOVE)
409 #  if defined(SPLICE_F_NONBLOCK)
410     INT_CONSTANT(SPLICE_F_NONBLOCK),
411 #  endif  // defined(SPLICE_F_NONBLOCK)
412 #  if defined(SPLICE_F_MORE)
413     INT_CONSTANT(SPLICE_F_MORE),
414 #  endif  // defined(SPLICE_F_MORE)
415 #  if defined(SPLICE_F_GIFT)
416     INT_CONSTANT(SPLICE_F_GIFT),
417 #  endif  // defined(SPLICE_F_GIFT)
418 #endif    // defined(XP_UNIX)
419 // copyfile
420 #if defined(COPYFILE_DATA)
421     INT_CONSTANT(COPYFILE_DATA),
422     INT_CONSTANT(COPYFILE_EXCL),
423     INT_CONSTANT(COPYFILE_XATTR),
424     INT_CONSTANT(COPYFILE_STAT),
425     INT_CONSTANT(COPYFILE_ACL),
426     INT_CONSTANT(COPYFILE_MOVE),
427 #endif  // defined(COPYFILE_DATA)
428 
429     // error values
430     INT_CONSTANT(EACCES),
431     INT_CONSTANT(EAGAIN),
432     INT_CONSTANT(EBADF),
433     INT_CONSTANT(EEXIST),
434     INT_CONSTANT(EFAULT),
435     INT_CONSTANT(EFBIG),
436     INT_CONSTANT(EINVAL),
437     INT_CONSTANT(EINTR),
438     INT_CONSTANT(EIO),
439     INT_CONSTANT(EISDIR),
440 #if defined(ELOOP)  // not defined with VC9
441     INT_CONSTANT(ELOOP),
442 #endif  // defined(ELOOP)
443     INT_CONSTANT(EMFILE),
444     INT_CONSTANT(ENAMETOOLONG),
445     INT_CONSTANT(ENFILE),
446     INT_CONSTANT(ENOENT),
447     INT_CONSTANT(ENOMEM),
448     INT_CONSTANT(ENOSPC),
449     INT_CONSTANT(ENOTDIR),
450     INT_CONSTANT(ENXIO),
451 #if defined(EOPNOTSUPP)  // not defined with VC 9
452     INT_CONSTANT(EOPNOTSUPP),
453 #endif                  // defined(EOPNOTSUPP)
454 #if defined(EOVERFLOW)  // not defined with VC 9
455     INT_CONSTANT(EOVERFLOW),
456 #endif  // defined(EOVERFLOW)
457     INT_CONSTANT(EPERM),
458     INT_CONSTANT(ERANGE),
459 #if defined(ETIMEDOUT)  // not defined with VC 9
460     INT_CONSTANT(ETIMEDOUT),
461 #endif                    // defined(ETIMEDOUT)
462 #if defined(EWOULDBLOCK)  // not defined with VC 9
463     INT_CONSTANT(EWOULDBLOCK),
464 #endif  // defined(EWOULDBLOCK)
465     INT_CONSTANT(EXDEV),
466 
467 #if defined(DT_UNKNOWN)
468     // Constants for |readdir|
469     INT_CONSTANT(DT_UNKNOWN),
470     INT_CONSTANT(DT_FIFO),
471     INT_CONSTANT(DT_CHR),
472     INT_CONSTANT(DT_DIR),
473     INT_CONSTANT(DT_BLK),
474     INT_CONSTANT(DT_REG),
475     INT_CONSTANT(DT_LNK),
476     INT_CONSTANT(DT_SOCK),
477 #endif  // defined(DT_UNKNOWN)
478 
479 #if defined(S_IFIFO)
480     // Constants for |stat|
481     INT_CONSTANT(S_IFMT),
482     INT_CONSTANT(S_IFIFO),
483     INT_CONSTANT(S_IFCHR),
484     INT_CONSTANT(S_IFDIR),
485     INT_CONSTANT(S_IFBLK),
486     INT_CONSTANT(S_IFREG),
487     INT_CONSTANT(S_IFLNK),
488     INT_CONSTANT(S_IFSOCK),
489 #endif  // defined(S_IFIFO)
490 
491     INT_CONSTANT(PATH_MAX),
492 
493 // Constants used to define data structures
494 //
495 // Many data structures have different fields/sizes/etc. on
496 // various OSes / versions of the same OS / platforms. For these
497 // data structures, we need to compute and export from C the size
498 // and, if necessary, the offset of fields, so as to be able to
499 // define the structure in JS.
500 
501 #if defined(XP_UNIX)
502     // The size of |mode_t|.
503     {"OSFILE_SIZEOF_MODE_T", JS::Int32Value(sizeof(mode_t))},
504 
505     // The size of |gid_t|.
506     {"OSFILE_SIZEOF_GID_T", JS::Int32Value(sizeof(gid_t))},
507 
508     // The size of |uid_t|.
509     {"OSFILE_SIZEOF_UID_T", JS::Int32Value(sizeof(uid_t))},
510 
511     // The size of |time_t|.
512     {"OSFILE_SIZEOF_TIME_T", JS::Int32Value(sizeof(time_t))},
513 
514     // The size of |fsblkcnt_t|.
515     {"OSFILE_SIZEOF_FSBLKCNT_T", JS::Int32Value(sizeof(fsblkcnt_t))},
516 
517 #  if !defined(ANDROID)
518     // The size of |posix_spawn_file_actions_t|.
519     {"OSFILE_SIZEOF_POSIX_SPAWN_FILE_ACTIONS_T",
520      JS::Int32Value(sizeof(posix_spawn_file_actions_t))},
521 
522     // The size of |posix_spawnattr_t|.
523     {"OSFILE_SIZEOF_POSIX_SPAWNATTR_T",
524      JS::Int32Value(sizeof(posix_spawnattr_t))},
525 #  endif  // !defined(ANDROID)
526 
527     // Defining |dirent|.
528     // Size
529     {"OSFILE_SIZEOF_DIRENT", JS::Int32Value(sizeof(dirent))},
530 
531     // Defining |flock|.
532     {"OSFILE_SIZEOF_FLOCK", JS::Int32Value(sizeof(struct flock))},
533     {"OSFILE_OFFSETOF_FLOCK_L_START",
534      JS::Int32Value(offsetof(struct flock, l_start))},
535     {"OSFILE_OFFSETOF_FLOCK_L_LEN",
536      JS::Int32Value(offsetof(struct flock, l_len))},
537     {"OSFILE_OFFSETOF_FLOCK_L_PID",
538      JS::Int32Value(offsetof(struct flock, l_pid))},
539     {"OSFILE_OFFSETOF_FLOCK_L_TYPE",
540      JS::Int32Value(offsetof(struct flock, l_type))},
541     {"OSFILE_OFFSETOF_FLOCK_L_WHENCE",
542      JS::Int32Value(offsetof(struct flock, l_whence))},
543 
544     // Offset of field |d_name|.
545     {"OSFILE_OFFSETOF_DIRENT_D_NAME",
546      JS::Int32Value(offsetof(struct dirent, d_name))},
547     // An upper bound to the length of field |d_name| of struct |dirent|.
548     // (may not be exact, depending on padding).
549     {"OSFILE_SIZEOF_DIRENT_D_NAME",
550      JS::Int32Value(sizeof(struct dirent) - offsetof(struct dirent, d_name))},
551 
552     // Defining |timeval|.
553     {"OSFILE_SIZEOF_TIMEVAL", JS::Int32Value(sizeof(struct timeval))},
554     {"OSFILE_OFFSETOF_TIMEVAL_TV_SEC",
555      JS::Int32Value(offsetof(struct timeval, tv_sec))},
556     {"OSFILE_OFFSETOF_TIMEVAL_TV_USEC",
557      JS::Int32Value(offsetof(struct timeval, tv_usec))},
558 
559 #  if defined(DT_UNKNOWN)
560     // Position of field |d_type| in |dirent|
561     // Not strictly posix, but seems defined on all platforms
562     // except mingw32.
563     {"OSFILE_OFFSETOF_DIRENT_D_TYPE",
564      JS::Int32Value(offsetof(struct dirent, d_type))},
565 #  endif  // defined(DT_UNKNOWN)
566 
567 // Under MacOS X and BSDs, |dirfd| is a macro rather than a
568 // function, so we need a little help to get it to work
569 #  if defined(dirfd)
570     {"OSFILE_SIZEOF_DIR", JS::Int32Value(sizeof(DIR))},
571 
572     {"OSFILE_OFFSETOF_DIR_DD_FD", JS::Int32Value(offsetof(DIR, __dd_fd))},
573 #  endif
574 
575     // Defining |stat|
576 
577     {"OSFILE_SIZEOF_STAT", JS::Int32Value(sizeof(struct stat))},
578 
579     {"OSFILE_OFFSETOF_STAT_ST_MODE",
580      JS::Int32Value(offsetof(struct stat, st_mode))},
581     {"OSFILE_OFFSETOF_STAT_ST_UID",
582      JS::Int32Value(offsetof(struct stat, st_uid))},
583     {"OSFILE_OFFSETOF_STAT_ST_GID",
584      JS::Int32Value(offsetof(struct stat, st_gid))},
585     {"OSFILE_OFFSETOF_STAT_ST_SIZE",
586      JS::Int32Value(offsetof(struct stat, st_size))},
587 
588 #  if defined(HAVE_ST_ATIMESPEC)
589     {"OSFILE_OFFSETOF_STAT_ST_ATIME",
590      JS::Int32Value(offsetof(struct stat, st_atimespec))},
591     {"OSFILE_OFFSETOF_STAT_ST_MTIME",
592      JS::Int32Value(offsetof(struct stat, st_mtimespec))},
593     {"OSFILE_OFFSETOF_STAT_ST_CTIME",
594      JS::Int32Value(offsetof(struct stat, st_ctimespec))},
595 #  else
596     {"OSFILE_OFFSETOF_STAT_ST_ATIME",
597      JS::Int32Value(offsetof(struct stat, st_atime))},
598     {"OSFILE_OFFSETOF_STAT_ST_MTIME",
599      JS::Int32Value(offsetof(struct stat, st_mtime))},
600     {"OSFILE_OFFSETOF_STAT_ST_CTIME",
601      JS::Int32Value(offsetof(struct stat, st_ctime))},
602 #  endif  // defined(HAVE_ST_ATIME)
603 
604 // Several OSes have a birthtime field. For the moment, supporting only Darwin.
605 #  if defined(_DARWIN_FEATURE_64_BIT_INODE)
606     {"OSFILE_OFFSETOF_STAT_ST_BIRTHTIME",
607      JS::Int32Value(offsetof(struct stat, st_birthtime))},
608 #  endif  // defined(_DARWIN_FEATURE_64_BIT_INODE)
609 
610     // Defining |statvfs|
611 
612     {"OSFILE_SIZEOF_STATVFS", JS::Int32Value(sizeof(struct statvfs))},
613 
614     // We have no guarantee how big "f_frsize" is, so we have to calculate that.
615     {"OSFILE_SIZEOF_STATVFS_F_FRSIZE",
616      JS::Int32Value(DOM_SIZEOF_MEMBER(struct statvfs, f_frsize))},
617     {"OSFILE_OFFSETOF_STATVFS_F_FRSIZE",
618      JS::Int32Value(offsetof(struct statvfs, f_frsize))},
619     {"OSFILE_OFFSETOF_STATVFS_F_BAVAIL",
620      JS::Int32Value(offsetof(struct statvfs, f_bavail))},
621 
622 #endif  // defined(XP_UNIX)
623 
624 // System configuration
625 
626 // Under MacOSX, to avoid using deprecated functions that do not
627 // match the constants we define in this object (including
628 // |sizeof|/|offsetof| stuff, but not only), for a number of
629 // functions, we need to adapt the name of the symbols we are using,
630 // whenever macro _DARWIN_FEATURE_64_BIT_INODE is set. We export
631 // this value to be able to do so from JavaScript.
632 #if defined(_DARWIN_FEATURE_64_BIT_INODE)
633     {"_DARWIN_FEATURE_64_BIT_INODE", JS::Int32Value(1)},
634 #endif  // defined(_DARWIN_FEATURE_64_BIT_INODE)
635 
636 // Similar feature for Linux
637 #if defined(_STAT_VER)
638     INT_CONSTANT(_STAT_VER),
639 #endif  // defined(_STAT_VER)
640 
641     PROP_END};
642 
643 #if defined(XP_WIN)
644 /**
645  * The properties defined in windows.h.
646  *
647  * If you extend this list of properties, please
648  * separate categories ("errors", "open", etc.),
649  * keep properties organized by alphabetical order
650  * and #ifdef-away properties that are not portable.
651  */
652 static const dom::ConstantSpec gWinProperties[] = {
653     // FormatMessage flags
654     INT_CONSTANT(FORMAT_MESSAGE_FROM_SYSTEM),
655     INT_CONSTANT(FORMAT_MESSAGE_IGNORE_INSERTS),
656 
657     // The max length of paths
658     INT_CONSTANT(MAX_PATH),
659 
660     // CreateFile desired access
661     INT_CONSTANT(GENERIC_ALL),
662     INT_CONSTANT(GENERIC_EXECUTE),
663     INT_CONSTANT(GENERIC_READ),
664     INT_CONSTANT(GENERIC_WRITE),
665 
666     // CreateFile share mode
667     INT_CONSTANT(FILE_SHARE_DELETE),
668     INT_CONSTANT(FILE_SHARE_READ),
669     INT_CONSTANT(FILE_SHARE_WRITE),
670 
671     // CreateFile creation disposition
672     INT_CONSTANT(CREATE_ALWAYS),
673     INT_CONSTANT(CREATE_NEW),
674     INT_CONSTANT(OPEN_ALWAYS),
675     INT_CONSTANT(OPEN_EXISTING),
676     INT_CONSTANT(TRUNCATE_EXISTING),
677 
678     // CreateFile attributes
679     INT_CONSTANT(FILE_ATTRIBUTE_ARCHIVE),
680     INT_CONSTANT(FILE_ATTRIBUTE_DIRECTORY),
681     INT_CONSTANT(FILE_ATTRIBUTE_HIDDEN),
682     INT_CONSTANT(FILE_ATTRIBUTE_NORMAL),
683     INT_CONSTANT(FILE_ATTRIBUTE_READONLY),
684     INT_CONSTANT(FILE_ATTRIBUTE_REPARSE_POINT),
685     INT_CONSTANT(FILE_ATTRIBUTE_SYSTEM),
686     INT_CONSTANT(FILE_ATTRIBUTE_TEMPORARY),
687     INT_CONSTANT(FILE_FLAG_BACKUP_SEMANTICS),
688 
689     // CreateFile error constant
690     {"INVALID_HANDLE_VALUE", JS::Int32Value(INT_PTR(INVALID_HANDLE_VALUE))},
691 
692     // CreateFile flags
693     INT_CONSTANT(FILE_FLAG_DELETE_ON_CLOSE),
694 
695     // SetFilePointer methods
696     INT_CONSTANT(FILE_BEGIN),
697     INT_CONSTANT(FILE_CURRENT),
698     INT_CONSTANT(FILE_END),
699 
700     // SetFilePointer error constant
701     UINT_CONSTANT(INVALID_SET_FILE_POINTER),
702 
703     // File attributes
704     INT_CONSTANT(FILE_ATTRIBUTE_DIRECTORY),
705 
706     // MoveFile flags
707     INT_CONSTANT(MOVEFILE_COPY_ALLOWED),
708     INT_CONSTANT(MOVEFILE_REPLACE_EXISTING),
709 
710     // GetFileAttributes error constant
711     INT_CONSTANT(INVALID_FILE_ATTRIBUTES),
712 
713     // GetNamedSecurityInfo and SetNamedSecurityInfo constants
714     INT_CONSTANT(UNPROTECTED_DACL_SECURITY_INFORMATION),
715     INT_CONSTANT(SE_FILE_OBJECT),
716     INT_CONSTANT(DACL_SECURITY_INFORMATION),
717 
718     // Errors
719     INT_CONSTANT(ERROR_INVALID_HANDLE),
720     INT_CONSTANT(ERROR_ACCESS_DENIED),
721     INT_CONSTANT(ERROR_DIR_NOT_EMPTY),
722     INT_CONSTANT(ERROR_FILE_EXISTS),
723     INT_CONSTANT(ERROR_ALREADY_EXISTS),
724     INT_CONSTANT(ERROR_FILE_NOT_FOUND),
725     INT_CONSTANT(ERROR_NO_MORE_FILES),
726     INT_CONSTANT(ERROR_PATH_NOT_FOUND),
727     INT_CONSTANT(ERROR_BAD_ARGUMENTS),
728     INT_CONSTANT(ERROR_SHARING_VIOLATION),
729     INT_CONSTANT(ERROR_NOT_SUPPORTED),
730 
731     PROP_END};
732 #endif  // defined(XP_WIN)
733 
734 /**
735  * Get a field of an object as an object.
736  *
737  * If the field does not exist, create it. If it exists but is not an
738  * object, throw a JS error.
739  */
GetOrCreateObjectProperty(JSContext * cx,JS::Handle<JSObject * > aObject,const char * aProperty)740 JSObject* GetOrCreateObjectProperty(JSContext* cx,
741                                     JS::Handle<JSObject*> aObject,
742                                     const char* aProperty) {
743   JS::Rooted<JS::Value> val(cx);
744   if (!JS_GetProperty(cx, aObject, aProperty, &val)) {
745     return nullptr;
746   }
747   if (!val.isUndefined()) {
748     if (val.isObject()) {
749       return &val.toObject();
750     }
751 
752     JS_ReportErrorNumberASCII(cx, js::GetErrorMessage, nullptr,
753                               JSMSG_UNEXPECTED_TYPE, aProperty,
754                               "not an object");
755     return nullptr;
756   }
757   return JS_DefineObject(cx, aObject, aProperty, nullptr, JSPROP_ENUMERATE);
758 }
759 
760 /**
761  * Set a property of an object from a nsString.
762  *
763  * If the nsString is void (i.e. IsVoid is true), do nothing.
764  */
SetStringProperty(JSContext * cx,JS::Handle<JSObject * > aObject,const char * aProperty,const nsString aValue)765 bool SetStringProperty(JSContext* cx, JS::Handle<JSObject*> aObject,
766                        const char* aProperty, const nsString aValue) {
767   if (aValue.IsVoid()) {
768     return true;
769   }
770   JSString* strValue = JS_NewUCStringCopyZ(cx, aValue.get());
771   NS_ENSURE_TRUE(strValue, false);
772   JS::Rooted<JS::Value> valValue(cx, JS::StringValue(strValue));
773   return JS_SetProperty(cx, aObject, aProperty, valValue);
774 }
775 
776 /**
777  * Define OS-specific constants.
778  *
779  * This function creates or uses JS object |OS.Constants| to store
780  * all its constants.
781  */
DefineOSFileConstants(JSContext * aCx,JS::Handle<JSObject * > aGlobal)782 bool OSFileConstantsService::DefineOSFileConstants(
783     JSContext* aCx, JS::Handle<JSObject*> aGlobal) {
784   if (!mInitialized) {
785     JS_ReportErrorNumberASCII(aCx, js::GetErrorMessage, nullptr,
786                               JSMSG_CANT_OPEN, "OSFileConstants",
787                               "initialization has failed");
788     return false;
789   }
790 
791   JS::Rooted<JSObject*> objOS(aCx);
792   if (!(objOS = GetOrCreateObjectProperty(aCx, aGlobal, "OS"))) {
793     return false;
794   }
795   JS::Rooted<JSObject*> objConstants(aCx);
796   if (!(objConstants = GetOrCreateObjectProperty(aCx, objOS, "Constants"))) {
797     return false;
798   }
799 
800   // Build OS.Constants.libc
801 
802   JS::Rooted<JSObject*> objLibc(aCx);
803   if (!(objLibc = GetOrCreateObjectProperty(aCx, objConstants, "libc"))) {
804     return false;
805   }
806   if (!dom::DefineConstants(aCx, objLibc, gLibcProperties)) {
807     return false;
808   }
809 
810 #if defined(XP_WIN)
811   // Build OS.Constants.Win
812 
813   JS::Rooted<JSObject*> objWin(aCx);
814   if (!(objWin = GetOrCreateObjectProperty(aCx, objConstants, "Win"))) {
815     return false;
816   }
817   if (!dom::DefineConstants(aCx, objWin, gWinProperties)) {
818     return false;
819   }
820 #endif  // defined(XP_WIN)
821 
822   // Build OS.Constants.Sys
823 
824   JS::Rooted<JSObject*> objSys(aCx);
825   if (!(objSys = GetOrCreateObjectProperty(aCx, objConstants, "Sys"))) {
826     return false;
827   }
828 
829   nsCOMPtr<nsIXULRuntime> runtime =
830       do_GetService(XULRUNTIME_SERVICE_CONTRACTID);
831   if (runtime) {
832     nsAutoCString os;
833     DebugOnly<nsresult> rv = runtime->GetOS(os);
834     MOZ_ASSERT(NS_SUCCEEDED(rv));
835 
836     JSString* strVersion = JS_NewStringCopyZ(aCx, os.get());
837     if (!strVersion) {
838       return false;
839     }
840 
841     JS::Rooted<JS::Value> valVersion(aCx, JS::StringValue(strVersion));
842     if (!JS_SetProperty(aCx, objSys, "Name", valVersion)) {
843       return false;
844     }
845   }
846 
847 #if defined(DEBUG)
848   JS::Rooted<JS::Value> valDebug(aCx, JS::TrueValue());
849   if (!JS_SetProperty(aCx, objSys, "DEBUG", valDebug)) {
850     return false;
851   }
852 #endif
853 
854 #if defined(HAVE_64BIT_BUILD)
855   JS::Rooted<JS::Value> valBits(aCx, JS::Int32Value(64));
856 #else
857   JS::Rooted<JS::Value> valBits(aCx, JS::Int32Value(32));
858 #endif  // defined (HAVE_64BIT_BUILD)
859   if (!JS_SetProperty(aCx, objSys, "bits", valBits)) {
860     return false;
861   }
862 
863   if (!JS_DefineProperty(
864           aCx, objSys, "umask", mUserUmask,
865           JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)) {
866     return false;
867   }
868 
869   // Build OS.Constants.Path
870 
871   JS::Rooted<JSObject*> objPath(aCx);
872   if (!(objPath = GetOrCreateObjectProperty(aCx, objConstants, "Path"))) {
873     return false;
874   }
875 
876   // Locate libxul
877   // Note that we don't actually provide the full path, only the name of the
878   // library, which is sufficient to link to the library using js-ctypes.
879 
880 #if defined(XP_MACOSX)
881   // Under MacOS X, for some reason, libxul is called simply "XUL",
882   // and we need to provide the full path.
883   nsAutoString libxul;
884   libxul.Append(mPaths->libDir);
885   libxul.AppendLiteral("/XUL");
886 #else
887   // On other platforms, libxul is a library "xul" with regular
888   // library prefix/suffix.
889   nsAutoString libxul;
890   libxul.AppendLiteral(MOZ_DLL_PREFIX);
891   libxul.AppendLiteral("xul");
892   libxul.AppendLiteral(MOZ_DLL_SUFFIX);
893 #endif  // defined(XP_MACOSX)
894 
895   if (!SetStringProperty(aCx, objPath, "libxul", libxul)) {
896     return false;
897   }
898 
899   if (!SetStringProperty(aCx, objPath, "libDir", mPaths->libDir)) {
900     return false;
901   }
902 
903   if (!SetStringProperty(aCx, objPath, "tmpDir", mPaths->tmpDir)) {
904     return false;
905   }
906 
907   // Configure profileDir only if it is available at this stage
908   if (!mPaths->profileDir.IsVoid() &&
909       !SetStringProperty(aCx, objPath, "profileDir", mPaths->profileDir)) {
910     return false;
911   }
912 
913   // Configure localProfileDir only if it is available at this stage
914   if (!mPaths->localProfileDir.IsVoid() &&
915       !SetStringProperty(aCx, objPath, "localProfileDir",
916                          mPaths->localProfileDir)) {
917     return false;
918   }
919 
920   // sqlite3 is linked from different places depending on the platform
921   nsAutoString libsqlite3;
922 #if defined(ANDROID)
923   // On Android, we use the system's libsqlite3
924   libsqlite3.AppendLiteral(MOZ_DLL_PREFIX);
925   libsqlite3.AppendLiteral("sqlite3");
926   libsqlite3.AppendLiteral(MOZ_DLL_SUFFIX);
927 #elif defined(XP_WIN)
928   // On Windows, for some reason, this is part of nss3.dll
929   libsqlite3.AppendLiteral(MOZ_DLL_PREFIX);
930   libsqlite3.AppendLiteral("nss3");
931   libsqlite3.AppendLiteral(MOZ_DLL_SUFFIX);
932 #else
933   // On other platforms, we link sqlite3 into libxul
934   libsqlite3 = libxul;
935 #endif  // defined(ANDROID) || defined(XP_WIN)
936 
937   if (!SetStringProperty(aCx, objPath, "libsqlite3", libsqlite3)) {
938     return false;
939   }
940 
941   return true;
942 }
943 
NS_IMPL_ISUPPORTS(OSFileConstantsService,nsIOSFileConstantsService,nsIObserver)944 NS_IMPL_ISUPPORTS(OSFileConstantsService, nsIOSFileConstantsService,
945                   nsIObserver)
946 
947 /* static */
948 already_AddRefed<OSFileConstantsService> OSFileConstantsService::GetOrCreate() {
949   if (!gInstance) {
950     MOZ_ASSERT(NS_IsMainThread());
951 
952     RefPtr<OSFileConstantsService> service = new OSFileConstantsService();
953     nsresult rv = service->InitOSFileConstants();
954     if (NS_WARN_IF(NS_FAILED(rv))) {
955       return nullptr;
956     }
957 
958     gInstance = std::move(service);
959     ClearOnShutdown(&gInstance);
960   }
961 
962   RefPtr<OSFileConstantsService> copy = gInstance;
963   return copy.forget();
964 }
965 
OSFileConstantsService()966 OSFileConstantsService::OSFileConstantsService()
967     : mInitialized(false), mUserUmask(0) {
968   MOZ_ASSERT(NS_IsMainThread());
969 }
970 
~OSFileConstantsService()971 OSFileConstantsService::~OSFileConstantsService() {
972   MOZ_ASSERT(NS_IsMainThread());
973 }
974 
975 NS_IMETHODIMP
Init(JSContext * aCx)976 OSFileConstantsService::Init(JSContext* aCx) {
977   MOZ_ASSERT(NS_IsMainThread());
978 
979   nsresult rv = InitOSFileConstants();
980   if (NS_FAILED(rv)) {
981     return rv;
982   }
983 
984   mozJSComponentLoader* loader = mozJSComponentLoader::Get();
985   JS::Rooted<JSObject*> targetObj(aCx);
986   loader->FindTargetObject(aCx, &targetObj);
987 
988   if (!DefineOSFileConstants(aCx, targetObj)) {
989     return NS_ERROR_FAILURE;
990   }
991 
992   return NS_OK;
993 }
994 
995 }  // namespace mozilla
996