MtpUtils.cpp 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. /*
  2. * Copyright (C) 2010 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. #define LOG_TAG "MtpUtils"
  17. #include <android-base/logging.h>
  18. #include <android-base/unique_fd.h>
  19. #include <dirent.h>
  20. #include <fcntl.h>
  21. #include <string>
  22. #include <sys/sendfile.h>
  23. #include <sys/stat.h>
  24. #include <sys/types.h>
  25. #include <stdio.h>
  26. #include <time.h>
  27. #include <unistd.h>
  28. #include "MtpUtils.h"
  29. using namespace std;
  30. namespace android {
  31. constexpr unsigned long FILE_COPY_SIZE = 262144;
  32. static void access_ok(const char *path) {
  33. if (access(path, F_OK) == -1) {
  34. // Ignore. Failure could be common in cases of delete where
  35. // the metadata was updated through other paths.
  36. }
  37. }
  38. /*
  39. DateTime strings follow a compatible subset of the definition found in ISO 8601, and
  40. take the form of a Unicode string formatted as: "YYYYMMDDThhmmss.s". In this
  41. representation, YYYY shall be replaced by the year, MM replaced by the month (01-12),
  42. DD replaced by the day (01-31), T is a constant character 'T' delimiting time from date,
  43. hh is replaced by the hour (00-23), mm is replaced by the minute (00-59), and ss by the
  44. second (00-59). The ".s" is optional, and represents tenths of a second.
  45. This is followed by a UTC offset given as "[+-]zzzz" or the literal "Z", meaning UTC.
  46. */
  47. bool parseDateTime(const char* dateTime, time_t& outSeconds) {
  48. int year, month, day, hour, minute, second;
  49. if (sscanf(dateTime, "%04d%02d%02dT%02d%02d%02d",
  50. &year, &month, &day, &hour, &minute, &second) != 6)
  51. return false;
  52. // skip optional tenth of second
  53. const char* tail = dateTime + 15;
  54. if (tail[0] == '.' && tail[1]) tail += 2;
  55. // FIXME: "Z" means UTC, but non-"Z" doesn't mean local time.
  56. // It might be that you're in Asia/Seoul on vacation and your Android
  57. // device has noticed this via the network, but your camera was set to
  58. // America/Los_Angeles once when you bought it and doesn't know where
  59. // it is right now, so the camera says "20160106T081700-0800" but we
  60. // just ignore the "-0800" and assume local time which is actually "+0900".
  61. // I think to support this (without switching to Java or using icu4c)
  62. // you'd want to always use timegm(3) and then manually add/subtract
  63. // the UTC offset parsed from the string (taking care of wrapping).
  64. // mktime(3) ignores the tm_gmtoff field, so you can't let it do the work.
  65. bool useUTC = (tail[0] == 'Z');
  66. struct tm tm = {};
  67. tm.tm_sec = second;
  68. tm.tm_min = minute;
  69. tm.tm_hour = hour;
  70. tm.tm_mday = day;
  71. tm.tm_mon = month - 1; // mktime uses months in 0 - 11 range
  72. tm.tm_year = year - 1900;
  73. tm.tm_isdst = -1;
  74. outSeconds = useUTC ? timegm(&tm) : mktime(&tm);
  75. return true;
  76. }
  77. void formatDateTime(time_t seconds, char* buffer, int bufferLength) {
  78. struct tm tm;
  79. localtime_r(&seconds, &tm);
  80. snprintf(buffer, bufferLength, "%04d%02d%02dT%02d%02d%02d",
  81. tm.tm_year + 1900,
  82. tm.tm_mon + 1, // localtime_r uses months in 0 - 11 range
  83. tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
  84. }
  85. int makeFolder(const char *path) {
  86. mode_t mask = umask(0);
  87. int ret = mkdir((const char *)path, DIR_PERM);
  88. umask(mask);
  89. if (ret && ret != -EEXIST) {
  90. PLOG(ERROR) << "Failed to create folder " << path;
  91. ret = -1;
  92. } else {
  93. chown((const char *)path, getuid(), FILE_GROUP);
  94. }
  95. access_ok(path);
  96. return ret;
  97. }
  98. /**
  99. * Copies target path and all children to destination path.
  100. *
  101. * Returns 0 on success or a negative value indicating number of failures
  102. */
  103. int copyRecursive(const char *fromPath, const char *toPath) {
  104. int ret = 0;
  105. string fromPathStr(fromPath);
  106. string toPathStr(toPath);
  107. DIR* dir = opendir(fromPath);
  108. if (!dir) {
  109. PLOG(ERROR) << "opendir " << fromPath << " failed";
  110. return -1;
  111. }
  112. if (fromPathStr[fromPathStr.size()-1] != '/')
  113. fromPathStr += '/';
  114. if (toPathStr[toPathStr.size()-1] != '/')
  115. toPathStr += '/';
  116. struct dirent* entry;
  117. while ((entry = readdir(dir))) {
  118. const char* name = entry->d_name;
  119. // ignore "." and ".."
  120. if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0))) {
  121. continue;
  122. }
  123. string oldFile = fromPathStr + name;
  124. string newFile = toPathStr + name;
  125. if (entry->d_type == DT_DIR) {
  126. ret += makeFolder(newFile.c_str());
  127. ret += copyRecursive(oldFile.c_str(), newFile.c_str());
  128. } else {
  129. ret += copyFile(oldFile.c_str(), newFile.c_str());
  130. }
  131. }
  132. return ret;
  133. }
  134. int copyFile(const char *fromPath, const char *toPath) {
  135. auto start = std::chrono::steady_clock::now();
  136. android::base::unique_fd fromFd(open(fromPath, O_RDONLY));
  137. if (fromFd == -1) {
  138. PLOG(ERROR) << "Failed to open copy from " << fromPath;
  139. return -1;
  140. }
  141. android::base::unique_fd toFd(open(toPath, O_CREAT | O_WRONLY, FILE_PERM));
  142. if (toFd == -1) {
  143. PLOG(ERROR) << "Failed to open copy to " << toPath;
  144. return -1;
  145. }
  146. off_t offset = 0;
  147. struct stat sstat = {};
  148. if (stat(fromPath, &sstat) == -1)
  149. return -1;
  150. off_t length = sstat.st_size;
  151. int ret = 0;
  152. while (offset < length) {
  153. ssize_t transfer_length = std::min(length - offset, (off_t) FILE_COPY_SIZE);
  154. ret = sendfile(toFd, fromFd, &offset, transfer_length);
  155. if (ret != transfer_length) {
  156. ret = -1;
  157. PLOG(ERROR) << "Copying failed!";
  158. break;
  159. }
  160. }
  161. auto end = std::chrono::steady_clock::now();
  162. std::chrono::duration<double> diff = end - start;
  163. LOG(DEBUG) << "Copied a file with MTP. Time: " << diff.count() << " s, Size: " << length <<
  164. ", Rate: " << ((double) length) / diff.count() << " bytes/s";
  165. chown(toPath, getuid(), FILE_GROUP);
  166. access_ok(toPath);
  167. return ret == -1 ? -1 : 0;
  168. }
  169. void deleteRecursive(const char* path) {
  170. string pathStr(path);
  171. if (pathStr[pathStr.size()-1] != '/') {
  172. pathStr += '/';
  173. }
  174. DIR* dir = opendir(path);
  175. if (!dir) {
  176. PLOG(ERROR) << "opendir " << path << " failed";
  177. return;
  178. }
  179. struct dirent* entry;
  180. while ((entry = readdir(dir))) {
  181. const char* name = entry->d_name;
  182. // ignore "." and ".."
  183. if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0))) {
  184. continue;
  185. }
  186. string childPath = pathStr + name;
  187. int success;
  188. if (entry->d_type == DT_DIR) {
  189. deleteRecursive(childPath.c_str());
  190. success = rmdir(childPath.c_str());
  191. } else {
  192. success = unlink(childPath.c_str());
  193. }
  194. access_ok(childPath.c_str());
  195. if (success == -1)
  196. PLOG(ERROR) << "Deleting path " << childPath << " failed";
  197. }
  198. closedir(dir);
  199. }
  200. bool deletePath(const char* path) {
  201. struct stat statbuf;
  202. int success;
  203. if (stat(path, &statbuf) == 0) {
  204. if (S_ISDIR(statbuf.st_mode)) {
  205. // rmdir will fail if the directory is non empty, so
  206. // there is no need to keep errors from deleteRecursive
  207. deleteRecursive(path);
  208. success = rmdir(path);
  209. } else {
  210. success = unlink(path);
  211. }
  212. } else {
  213. PLOG(ERROR) << "deletePath stat failed for " << path;
  214. return false;
  215. }
  216. if (success == -1)
  217. PLOG(ERROR) << "Deleting path " << path << " failed";
  218. access_ok(path);
  219. return success == 0;
  220. }
  221. int renameTo(const char *oldPath, const char *newPath) {
  222. int ret = rename(oldPath, newPath);
  223. access_ok(oldPath);
  224. access_ok(newPath);
  225. return ret;
  226. }
  227. // Calls access(2) on the path to update underlying filesystems,
  228. // then closes the fd.
  229. void closeObjFd(int fd, const char *path) {
  230. close(fd);
  231. access_ok(path);
  232. }
  233. } // namespace android