os_compat_android.cc revision 23730a6e56a168d1879203e4b3819bb36e3d8f1f
1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "base/os_compat_android.h"
6
7#include <asm/unistd.h>
8#include <errno.h>
9#include <math.h>
10#include <sys/stat.h>
11#include <sys/syscall.h>
12#include <time64.h>
13
14#include "base/rand_util.h"
15#include "base/strings/string_piece.h"
16#include "base/strings/stringprintf.h"
17
18extern "C" {
19// There is no futimes() avaiable in Bionic, so we provide our own
20// implementation until it is there.
21int futimes(int fd, const struct timeval tv[2]) {
22  if (tv == NULL)
23    return syscall(__NR_utimensat, fd, NULL, NULL, 0);
24
25  if (tv[0].tv_usec < 0 || tv[0].tv_usec >= 1000000 ||
26      tv[1].tv_usec < 0 || tv[1].tv_usec >= 1000000) {
27    errno = EINVAL;
28    return -1;
29  }
30
31  // Convert timeval to timespec.
32  struct timespec ts[2];
33  ts[0].tv_sec = tv[0].tv_sec;
34  ts[0].tv_nsec = tv[0].tv_usec * 1000;
35  ts[1].tv_sec = tv[1].tv_sec;
36  ts[1].tv_nsec = tv[1].tv_usec * 1000;
37  return syscall(__NR_utimensat, fd, NULL, ts, 0);
38}
39
40// Android has only timegm64() and no timegm().
41// We replicate the behaviour of timegm() when the result overflows time_t.
42time_t timegm(struct tm* const t) {
43  // time_t is signed on Android.
44  static const time_t kTimeMax = ~(1L << (sizeof(time_t) * CHAR_BIT - 1));
45  static const time_t kTimeMin = (1L << (sizeof(time_t) * CHAR_BIT - 1));
46  time64_t result = timegm64(t);
47  if (result < kTimeMin || result > kTimeMax)
48    return -1;
49  return result;
50}
51
52// The following is only needed when building with GCC 4.6 or higher
53// (i.e. not with Android GCC 4.4.3, nor with Clang).
54//
55// GCC is now capable of optimizing successive calls to sin() and cos() into
56// a single call to sincos(). This means that source code that looks like:
57//
58//     double c, s;
59//     c = cos(angle);
60//     s = sin(angle);
61//
62// Will generate machine code that looks like:
63//
64//     double c, s;
65//     sincos(angle, &s, &c);
66//
67// Unfortunately, sincos() and friends are not part of the Android libm.so
68// library provided by the NDK for API level 9. When the optimization kicks
69// in, it makes the final build fail with a puzzling message (puzzling
70// because 'sincos' doesn't appear anywhere in the sources!).
71//
72// To solve this, we provide our own implementation of the sincos() function
73// and related friends. Note that we must also explicitely tell GCC to disable
74// optimizations when generating these. Otherwise, the generated machine code
75// for each function would simply end up calling itself, resulting in a
76// runtime crash due to stack overflow.
77//
78#if defined(__GNUC__) && !defined(__clang__) && \
79    !defined(ANDROID_SINCOS_PROVIDED)
80
81// For the record, Clang does not support the 'optimize' attribute.
82// In the unlikely event that it begins performing this optimization too,
83// we'll have to find a different way to achieve this. NOTE: Tested with O1
84// which still performs the optimization.
85//
86#define GCC_NO_OPTIMIZE  __attribute__((optimize("O0")))
87
88GCC_NO_OPTIMIZE
89void sincos(double angle, double* s, double *c) {
90  *c = cos(angle);
91  *s = sin(angle);
92}
93
94GCC_NO_OPTIMIZE
95void sincosf(float angle, float* s, float* c) {
96  *c = cosf(angle);
97  *s = sinf(angle);
98}
99
100#endif // __GNUC__ && !__clang__
101
102// An implementation of mkdtemp, since it is not exposed by the NDK
103// for native API level 9 that we target.
104//
105// For any changes in the mkdtemp function, you should manually run the unittest
106// OsCompatAndroidTest.DISABLED_TestMkdTemp in your local machine to check if it
107// passes. Please don't enable it, since it creates a directory and may be
108// source of flakyness.
109char* mkdtemp(char* path) {
110  if (path == NULL) {
111    errno = EINVAL;
112    return NULL;
113  }
114
115  const int path_len = strlen(path);
116
117  // The last six characters of 'path' must be XXXXXX.
118  const base::StringPiece kSuffix("XXXXXX");
119  const int kSuffixLen = kSuffix.length();
120  if (!base::StringPiece(path, path_len).ends_with(kSuffix)) {
121    errno = EINVAL;
122    return NULL;
123  }
124
125  // If the path contains a directory, as in /tmp/foo/XXXXXXXX, make sure
126  // that /tmp/foo exists, otherwise we're going to loop a really long
127  // time for nothing below
128  char* dirsep = strrchr(path, '/');
129  if (dirsep != NULL) {
130    struct stat st;
131    int ret;
132
133    *dirsep = '\0';  // Terminating directory path temporarily
134
135    ret = stat(path, &st);
136
137    *dirsep = '/';  // Restoring directory separator
138    if (ret < 0)  // Directory probably does not exist
139      return NULL;
140    if (!S_ISDIR(st.st_mode)) {  // Not a directory
141      errno = ENOTDIR;
142      return NULL;
143    }
144  }
145
146  // Max number of tries using different random suffixes.
147  const int kMaxTries = 100;
148
149  // Now loop until we CAN create a directory by that name or we reach the max
150  // number of tries.
151  for (int i = 0; i < kMaxTries; ++i) {
152    // Fill the suffix XXXXXX with a random string composed of a-z chars.
153    for (int pos = 0; pos < kSuffixLen; ++pos) {
154      char rand_char = static_cast<char>(base::RandInt('a', 'z'));
155      path[path_len - kSuffixLen + pos] = rand_char;
156    }
157    if (mkdir(path, 0700) == 0) {
158      // We just created the directory succesfully.
159      return path;
160    }
161    if (errno != EEXIST) {
162      // The directory doesn't exist, but an error occured
163      return NULL;
164    }
165  }
166
167  // We reached the max number of tries.
168  return NULL;
169}
170
171}  // extern "C"
172