adb_utils.cpp revision 706955ff0d159d28bb6eb06dc10178624c505f4e
1/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#define TRACE_TAG ADB
18
19#include "adb_utils.h"
20
21#include <libgen.h>
22#include <stdlib.h>
23#include <sys/stat.h>
24#include <sys/types.h>
25#include <unistd.h>
26
27#include <algorithm>
28
29#include <android-base/logging.h>
30#include <android-base/stringprintf.h>
31#include <android-base/strings.h>
32
33#include "adb.h"
34#include "adb_trace.h"
35#include "sysdeps.h"
36
37ADB_MUTEX_DEFINE(basename_lock);
38ADB_MUTEX_DEFINE(dirname_lock);
39
40#if defined(_WIN32)
41constexpr char kNullFileName[] = "NUL";
42#else
43constexpr char kNullFileName[] = "/dev/null";
44#endif
45
46void close_stdin() {
47    int fd = unix_open(kNullFileName, O_RDONLY);
48    if (fd == -1) {
49        fatal_errno("failed to open %s", kNullFileName);
50    }
51
52    if (TEMP_FAILURE_RETRY(dup2(fd, STDIN_FILENO)) == -1) {
53        fatal_errno("failed to redirect stdin to %s", kNullFileName);
54    }
55    unix_close(fd);
56}
57
58bool getcwd(std::string* s) {
59  char* cwd = getcwd(nullptr, 0);
60  if (cwd != nullptr) *s = cwd;
61  free(cwd);
62  return (cwd != nullptr);
63}
64
65bool directory_exists(const std::string& path) {
66  struct stat sb;
67  return lstat(path.c_str(), &sb) != -1 && S_ISDIR(sb.st_mode);
68}
69
70std::string escape_arg(const std::string& s) {
71  std::string result = s;
72
73  // Escape any ' in the string (before we single-quote the whole thing).
74  // The correct way to do this for the shell is to replace ' with '\'' --- that is,
75  // close the existing single-quoted string, escape a single single-quote, and start
76  // a new single-quoted string. Like the C preprocessor, the shell will concatenate
77  // these pieces into one string.
78  for (size_t i = 0; i < s.size(); ++i) {
79    if (s[i] == '\'') {
80      result.insert(i, "'\\'");
81      i += 2;
82    }
83  }
84
85  // Prefix and suffix the whole string with '.
86  result.insert(result.begin(), '\'');
87  result.push_back('\'');
88  return result;
89}
90
91std::string adb_basename(const std::string& path) {
92  // Copy path because basename may modify the string passed in.
93  std::string result(path);
94
95  // Use lock because basename() may write to a process global and return a
96  // pointer to that. Note that this locking strategy only works if all other
97  // callers to dirname in the process also grab this same lock.
98  adb_mutex_lock(&basename_lock);
99
100  // Note that if std::string uses copy-on-write strings, &str[0] will cause
101  // the copy to be made, so there is no chance of us accidentally writing to
102  // the storage for 'path'.
103  char* name = basename(&result[0]);
104
105  // In case dirname returned a pointer to a process global, copy that string
106  // before leaving the lock.
107  result.assign(name);
108
109  adb_mutex_unlock(&basename_lock);
110
111  return result;
112}
113
114std::string adb_dirname(const std::string& path) {
115  // Copy path because dirname may modify the string passed in.
116  std::string result(path);
117
118  // Use lock because dirname() may write to a process global and return a
119  // pointer to that. Note that this locking strategy only works if all other
120  // callers to dirname in the process also grab this same lock.
121  adb_mutex_lock(&dirname_lock);
122
123  // Note that if std::string uses copy-on-write strings, &str[0] will cause
124  // the copy to be made, so there is no chance of us accidentally writing to
125  // the storage for 'path'.
126  char* parent = dirname(&result[0]);
127
128  // In case dirname returned a pointer to a process global, copy that string
129  // before leaving the lock.
130  result.assign(parent);
131
132  adb_mutex_unlock(&dirname_lock);
133
134  return result;
135}
136
137// Given a relative or absolute filepath, create the parent directory hierarchy
138// as needed. Returns true if the hierarchy is/was setup.
139bool mkdirs(const std::string& path) {
140  // TODO: all the callers do unlink && mkdirs && adb_creat ---
141  // that's probably the operation we should expose.
142
143  // Implementation Notes:
144  //
145  // Pros:
146  // - Uses dirname, so does not need to deal with OS_PATH_SEPARATOR.
147  // - On Windows, uses mingw dirname which accepts '/' and '\\', drive letters
148  //   (C:\foo), UNC paths (\\server\share\dir\dir\file), and Unicode (when
149  //   combined with our adb_mkdir() which takes UTF-8).
150  // - Is optimistic wrt thinking that a deep directory hierarchy will exist.
151  //   So it does as few stat()s as possible before doing mkdir()s.
152  // Cons:
153  // - Recursive, so it uses stack space relative to number of directory
154  //   components.
155
156  if (directory_exists(path)) {
157    return true;
158  }
159
160  // If dirname returned the same path as what we passed in, don't go recursive.
161  // This can happen on Windows when walking up the directory hierarchy and not
162  // finding anything that already exists (unlike POSIX that will eventually
163  // find . or /).
164  const std::string parent(adb_dirname(path));
165
166  if (parent == path) {
167    errno = ENOENT;
168    return false;
169  }
170
171  // Recursively make parent directories of 'path'.
172  if (!mkdirs(parent)) {
173    return false;
174  }
175
176  // Now that the parent directory hierarchy of 'path' has been ensured,
177  // create parent itself.
178  if (adb_mkdir(path, 0775) == -1) {
179    // Can't just check for errno == EEXIST because it might be a file that
180    // exists.
181    const int saved_errno = errno;
182    if (directory_exists(parent)) {
183      return true;
184    }
185    errno = saved_errno;
186    return false;
187  }
188
189  return true;
190}
191
192std::string dump_hex(const void* data, size_t byte_count) {
193    byte_count = std::min(byte_count, size_t(16));
194
195    const uint8_t* p = reinterpret_cast<const uint8_t*>(data);
196
197    std::string line;
198    for (size_t i = 0; i < byte_count; ++i) {
199        android::base::StringAppendF(&line, "%02x", p[i]);
200    }
201    line.push_back(' ');
202
203    for (size_t i = 0; i < byte_count; ++i) {
204        int ch = p[i];
205        line.push_back(isprint(ch) ? ch : '.');
206    }
207
208    return line;
209}
210
211std::string perror_str(const char* msg) {
212    return android::base::StringPrintf("%s: %s", msg, strerror(errno));
213}
214
215#if !defined(_WIN32)
216bool set_file_block_mode(int fd, bool block) {
217    int flags = fcntl(fd, F_GETFL, 0);
218    if (flags == -1) {
219        PLOG(ERROR) << "failed to fcntl(F_GETFL) for fd " << fd;
220        return false;
221    }
222    flags = block ? (flags & ~O_NONBLOCK) : (flags | O_NONBLOCK);
223    if (fcntl(fd, F_SETFL, flags) != 0) {
224        PLOG(ERROR) << "failed to fcntl(F_SETFL) for fd " << fd << ", flags " << flags;
225        return false;
226    }
227    return true;
228}
229#endif
230