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