blob: 036ffe74c1efc43f999fd97e35dc942d8d77847f [file] [log] [blame]
Mike Lockwood16864ba2010-05-11 17:16:59 -04001/*
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
Mike Lockwoodb14e5882010-06-29 18:11:52 -040017#define LOG_TAG "MtpUtils"
18
Jerry Zhang708b3e02017-09-26 17:53:39 -070019#include <android-base/logging.h>
20#include <android-base/unique_fd.h>
21#include <dirent.h>
22#include <fcntl.h>
23#include <sys/sendfile.h>
24#include <sys/stat.h>
25#include <sys/types.h>
Mike Lockwood335dd2b2010-05-19 10:33:39 -040026#include <stdio.h>
Mike Lockwood16864ba2010-05-11 17:16:59 -040027#include <time.h>
Jerry Zhang708b3e02017-09-26 17:53:39 -070028#include <unistd.h>
Mike Lockwood16864ba2010-05-11 17:16:59 -040029
Mike Lockwood16864ba2010-05-11 17:16:59 -040030#include "MtpUtils.h"
31
Mike Lockwood7850ef92010-05-14 10:10:36 -040032namespace android {
33
Jerry Zhang708b3e02017-09-26 17:53:39 -070034constexpr unsigned long FILE_COPY_SIZE = 262144;
35
Mike Lockwood16864ba2010-05-11 17:16:59 -040036/*
37DateTime strings follow a compatible subset of the definition found in ISO 8601, and
38take the form of a Unicode string formatted as: "YYYYMMDDThhmmss.s". In this
39representation, YYYY shall be replaced by the year, MM replaced by the month (01-12),
40DD replaced by the day (01-31), T is a constant character 'T' delimiting time from date,
41hh is replaced by the hour (00-23), mm is replaced by the minute (00-59), and ss by the
42second (00-59). The ".s" is optional, and represents tenths of a second.
Elliott Hughesd4b47382016-01-05 17:05:47 -080043This is followed by a UTC offset given as "[+-]zzzz" or the literal "Z", meaning UTC.
Mike Lockwood16864ba2010-05-11 17:16:59 -040044*/
45
46bool parseDateTime(const char* dateTime, time_t& outSeconds) {
47 int year, month, day, hour, minute, second;
Mike Lockwood16864ba2010-05-11 17:16:59 -040048 if (sscanf(dateTime, "%04d%02d%02dT%02d%02d%02d",
Elliott Hughesd4b47382016-01-05 17:05:47 -080049 &year, &month, &day, &hour, &minute, &second) != 6)
Mike Lockwood16864ba2010-05-11 17:16:59 -040050 return false;
Elliott Hughesd4b47382016-01-05 17:05:47 -080051
Mike Lockwood16864ba2010-05-11 17:16:59 -040052 // skip optional tenth of second
Elliott Hughesd4b47382016-01-05 17:05:47 -080053 const char* tail = dateTime + 15;
54 if (tail[0] == '.' && tail[1]) tail += 2;
55
56 // FIXME: "Z" means UTC, but non-"Z" doesn't mean local time.
57 // It might be that you're in Asia/Seoul on vacation and your Android
58 // device has noticed this via the network, but your camera was set to
59 // America/Los_Angeles once when you bought it and doesn't know where
60 // it is right now, so the camera says "20160106T081700-0800" but we
61 // just ignore the "-0800" and assume local time which is actually "+0900".
62 // I think to support this (without switching to Java or using icu4c)
63 // you'd want to always use timegm(3) and then manually add/subtract
64 // the UTC offset parsed from the string (taking care of wrapping).
65 // mktime(3) ignores the tm_gmtoff field, so you can't let it do the work.
Mike Lockwood16864ba2010-05-11 17:16:59 -040066 bool useUTC = (tail[0] == 'Z');
67
Elliott Hughesd4b47382016-01-05 17:05:47 -080068 struct tm tm = {};
Mike Lockwood16864ba2010-05-11 17:16:59 -040069 tm.tm_sec = second;
70 tm.tm_min = minute;
71 tm.tm_hour = hour;
72 tm.tm_mday = day;
Mike Lockwoodea1db0a2011-01-26 14:41:45 -080073 tm.tm_mon = month - 1; // mktime uses months in 0 - 11 range
Mike Lockwood16864ba2010-05-11 17:16:59 -040074 tm.tm_year = year - 1900;
Mike Lockwood16864ba2010-05-11 17:16:59 -040075 tm.tm_isdst = -1;
Elliott Hughesd4b47382016-01-05 17:05:47 -080076 outSeconds = useUTC ? timegm(&tm) : mktime(&tm);
Mike Lockwood16864ba2010-05-11 17:16:59 -040077
78 return true;
79}
80
81void formatDateTime(time_t seconds, char* buffer, int bufferLength) {
82 struct tm tm;
83
84 localtime_r(&seconds, &tm);
85 snprintf(buffer, bufferLength, "%04d%02d%02dT%02d%02d%02d",
Elliott Hughesd4b47382016-01-05 17:05:47 -080086 tm.tm_year + 1900,
Mike Lockwoodea1db0a2011-01-26 14:41:45 -080087 tm.tm_mon + 1, // localtime_r uses months in 0 - 11 range
88 tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
Mike Lockwood16864ba2010-05-11 17:16:59 -040089}
90
Jerry Zhang708b3e02017-09-26 17:53:39 -070091int copyFile(const char *fromPath, const char *toPath) {
92 auto start = std::chrono::steady_clock::now();
93
94 android::base::unique_fd fromFd(open(fromPath, O_RDONLY));
95 if (fromFd == -1) {
96 PLOG(ERROR) << "Failed to open copy from " << fromPath;
97 return -1;
98 }
99 android::base::unique_fd toFd(open(toPath, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR));
100 if (toFd == -1) {
101 PLOG(ERROR) << "Failed to open copy to " << toPath;
102 return -1;
103 }
104 off_t offset = 0;
105
106 struct stat sstat = {};
107 if (stat(fromPath, &sstat) == -1)
108 return -1;
109
110 off_t length = sstat.st_size;
111 int ret = 0;
112
113 while (offset < length) {
114 ssize_t transfer_length = std::min(length - offset, (off_t) FILE_COPY_SIZE);
115 ret = sendfile(toFd, fromFd, &offset, transfer_length);
116 if (ret != transfer_length) {
117 ret = -1;
118 PLOG(ERROR) << "Copying failed!";
119 break;
120 }
121 }
122 auto end = std::chrono::steady_clock::now();
123 std::chrono::duration<double> diff = end - start;
124 LOG(INFO) << "Copied a file with MTP. Time: " << diff.count() << " s, Size: " << length <<
125 ", Rate: " << ((double) length) / diff.count() << " bytes/s";
126 return ret == -1 ? -1 : 0;
127}
128
129void deleteRecursive(const char* path) {
130 char pathbuf[PATH_MAX];
131 size_t pathLength = strlen(path);
132 if (pathLength >= sizeof(pathbuf) - 1) {
133 LOG(ERROR) << "path too long: " << path;
134 }
135 strcpy(pathbuf, path);
136 if (pathbuf[pathLength - 1] != '/') {
137 pathbuf[pathLength++] = '/';
138 }
139 char* fileSpot = pathbuf + pathLength;
140 int pathRemaining = sizeof(pathbuf) - pathLength - 1;
141
142 DIR* dir = opendir(path);
143 if (!dir) {
144 PLOG(ERROR) << "opendir " << path << " failed";
145 return;
146 }
147
148 struct dirent* entry;
149 while ((entry = readdir(dir))) {
150 const char* name = entry->d_name;
151
152 // ignore "." and ".."
153 if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0))) {
154 continue;
155 }
156
157 int nameLength = strlen(name);
158 if (nameLength > pathRemaining) {
159 LOG(ERROR) << "path " << path << "/" << name << " too long";
160 continue;
161 }
162 strcpy(fileSpot, name);
163
164 if (entry->d_type == DT_DIR) {
165 deleteRecursive(pathbuf);
166 rmdir(pathbuf);
167 } else {
168 unlink(pathbuf);
169 }
170 }
171 closedir(dir);
172}
173
174void deletePath(const char* path) {
175 struct stat statbuf;
176 if (stat(path, &statbuf) == 0) {
177 if (S_ISDIR(statbuf.st_mode)) {
178 deleteRecursive(path);
179 rmdir(path);
180 } else {
181 unlink(path);
182 }
183 } else {
184 PLOG(ERROR) << "deletePath stat failed for " << path;;
185 }
186}
187
Mike Lockwood7850ef92010-05-14 10:10:36 -0400188} // namespace android