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