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