Account for folders in copy and move.
Copy and move will use mkdir() if target
is a folder, and copyFile() if target is a file.
Move will recursively copy contents if moving
between different storages. Move will also
change the storageId.
Bug: 67028892
Test: Copy and move folders on win 10
Change-Id: If114ef74b9d8668cf66d45953d9ea8b17bc11ae8
diff --git a/media/mtp/MtpDatabase.h b/media/mtp/MtpDatabase.h
index c7021df..2395f4f 100644
--- a/media/mtp/MtpDatabase.h
+++ b/media/mtp/MtpDatabase.h
@@ -104,7 +104,7 @@
virtual MtpProperty* getDevicePropertyDesc(MtpDeviceProperty property) = 0;
virtual MtpResponseCode moveObject(MtpObjectHandle handle, MtpObjectHandle newParent,
- MtpString& newPath) = 0;
+ MtpStorageID newStorage, MtpString& newPath) = 0;
virtual void sessionStarted() = 0;
diff --git a/media/mtp/MtpServer.cpp b/media/mtp/MtpServer.cpp
index 236f3a9..a0944a9 100644
--- a/media/mtp/MtpServer.cpp
+++ b/media/mtp/MtpServer.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <android-base/logging.h>
#include <android-base/properties.h>
#include <chrono>
#include <dirent.h>
@@ -99,16 +100,12 @@
};
MtpServer::MtpServer(MtpDatabase* database, bool ptp,
- int fileGroup, int filePerm, int directoryPerm,
const MtpString& deviceInfoManufacturer,
const MtpString& deviceInfoModel,
const MtpString& deviceInfoDeviceVersion,
const MtpString& deviceInfoSerialNumber)
: mDatabase(database),
mPtp(ptp),
- mFileGroup(fileGroup),
- mFilePermission(filePerm),
- mDirectoryPermission(directoryPerm),
mDeviceInfoManufacturer(deviceInfoManufacturer),
mDeviceInfoModel(deviceInfoModel),
mDeviceInfoDeviceVersion(deviceInfoDeviceVersion),
@@ -1002,12 +999,9 @@
}
if (format == MTP_FORMAT_ASSOCIATION) {
- mode_t mask = umask(0);
- int ret = mkdir((const char *)path, mDirectoryPermission);
- umask(mask);
- if (ret && ret != -EEXIST)
+ int ret = makeFolder((const char *)path);
+ if (ret)
return MTP_RESPONSE_GENERAL_ERROR;
- chown((const char *)path, getuid(), mFileGroup);
// SendObject does not get sent for directories, so call endSendObject here instead
mDatabase->endSendObject(path, handle, MTP_FORMAT_ASSOCIATION, MTP_RESPONSE_OK);
@@ -1068,28 +1062,39 @@
path += "/";
path += info.mName;
- result = mDatabase->moveObject(objectHandle, parent, path);
+ result = mDatabase->moveObject(objectHandle, parent, storageID, path);
if (result != MTP_RESPONSE_OK)
return result;
if (info.mStorageID == storageID) {
ALOGV("Moving file from %s to %s", (const char*)fromPath, (const char*)path);
if (rename(fromPath, path)) {
- ALOGE("rename() failed from %s to %s", (const char*)fromPath, (const char*)path);
+ PLOG(ERROR) << "rename() failed from " << fromPath << " to " << path;
result = MTP_RESPONSE_GENERAL_ERROR;
}
} else {
ALOGV("Moving across storages from %s to %s", (const char*)fromPath, (const char*)path);
- if (copyFile(fromPath, path)) {
- result = MTP_RESPONSE_GENERAL_ERROR;
+ if (format == MTP_FORMAT_ASSOCIATION) {
+ int ret = makeFolder((const char *)path);
+ ret += copyRecursive(fromPath, path);
+ if (ret) {
+ result = MTP_RESPONSE_GENERAL_ERROR;
+ } else {
+ deletePath(fromPath);
+ }
} else {
- deletePath(fromPath);
+ if (copyFile(fromPath, path)) {
+ result = MTP_RESPONSE_GENERAL_ERROR;
+ } else {
+ deletePath(fromPath);
+ }
}
}
// If the move failed, undo the database change
if (result != MTP_RESPONSE_OK)
- if (mDatabase->moveObject(objectHandle, info.mParent, fromPath) != MTP_RESPONSE_OK)
+ if (mDatabase->moveObject(objectHandle, info.mParent, info.mStorageID,
+ fromPath) != MTP_RESPONSE_OK)
ALOGE("Couldn't undo failed move");
return result;
@@ -1148,8 +1153,15 @@
}
ALOGV("Copying file from %s to %s", (const char*)fromPath, (const char*)path);
- if (copyFile(fromPath, path)) {
- result = MTP_RESPONSE_GENERAL_ERROR;
+ if (format == MTP_FORMAT_ASSOCIATION) {
+ int ret = makeFolder((const char *)path);
+ if (ret) {
+ result = MTP_RESPONSE_GENERAL_ERROR;
+ }
+ } else {
+ if (copyFile(fromPath, path)) {
+ result = MTP_RESPONSE_GENERAL_ERROR;
+ }
}
mDatabase->endSendObject(path, handle, format, result);
@@ -1188,10 +1200,10 @@
result = MTP_RESPONSE_GENERAL_ERROR;
goto done;
}
- fchown(mfr.fd, getuid(), mFileGroup);
+ fchown(mfr.fd, getuid(), FILE_GROUP);
// set permissions
mask = umask(0);
- fchmod(mfr.fd, mFilePermission);
+ fchmod(mfr.fd, FILE_PERM);
umask(mask);
if (initialData > 0) {
diff --git a/media/mtp/MtpServer.h b/media/mtp/MtpServer.h
index aafc753..0204b09 100644
--- a/media/mtp/MtpServer.h
+++ b/media/mtp/MtpServer.h
@@ -43,12 +43,6 @@
// appear as a PTP device
bool mPtp;
- // group to own new files and folders
- int mFileGroup;
- // permissions for new files and directories
- int mFilePermission;
- int mDirectoryPermission;
-
// Manufacturer to report in DeviceInfo
MtpString mDeviceInfoManufacturer;
// Model to report in DeviceInfo
@@ -105,7 +99,6 @@
public:
MtpServer(MtpDatabase* database, bool ptp,
- int fileGroup, int filePerm, int directoryPerm,
const MtpString& deviceInfoManufacturer,
const MtpString& deviceInfoModel,
const MtpString& deviceInfoDeviceVersion,
diff --git a/media/mtp/MtpUtils.cpp b/media/mtp/MtpUtils.cpp
index 036ffe7..3f5648b 100644
--- a/media/mtp/MtpUtils.cpp
+++ b/media/mtp/MtpUtils.cpp
@@ -20,6 +20,7 @@
#include <android-base/unique_fd.h>
#include <dirent.h>
#include <fcntl.h>
+#include <string>
#include <sys/sendfile.h>
#include <sys/stat.h>
#include <sys/types.h>
@@ -29,6 +30,8 @@
#include "MtpUtils.h"
+using namespace std;
+
namespace android {
constexpr unsigned long FILE_COPY_SIZE = 262144;
@@ -88,6 +91,60 @@
tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
}
+int makeFolder(const char *path) {
+ mode_t mask = umask(0);
+ int ret = mkdir((const char *)path, DIR_PERM);
+ umask(mask);
+ if (ret && ret != -EEXIST) {
+ PLOG(ERROR) << "Failed to create folder " << path;
+ ret = -1;
+ } else {
+ chown((const char *)path, getuid(), FILE_GROUP);
+ }
+ return ret;
+}
+
+/**
+ * Copies target path and all children to destination path.
+ *
+ * Returns 0 on success or a negative value indicating number of failures
+ */
+int copyRecursive(const char *fromPath, const char *toPath) {
+ int ret = 0;
+ string fromPathStr(fromPath);
+ string toPathStr(toPath);
+
+ DIR* dir = opendir(fromPath);
+ if (!dir) {
+ PLOG(ERROR) << "opendir " << fromPath << " failed";
+ return -1;
+ }
+ if (fromPathStr[fromPathStr.size()-1] != '/')
+ fromPathStr += '/';
+ if (toPathStr[toPathStr.size()-1] != '/')
+ toPathStr += '/';
+
+ struct dirent* entry;
+ while ((entry = readdir(dir))) {
+ const char* name = entry->d_name;
+
+ // ignore "." and ".."
+ if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0))) {
+ continue;
+ }
+ string oldFile = fromPathStr + name;
+ string newFile = toPathStr + name;
+
+ if (entry->d_type == DT_DIR) {
+ ret += makeFolder(newFile.c_str());
+ ret += copyRecursive(oldFile.c_str(), newFile.c_str());
+ } else {
+ ret += copyFile(oldFile.c_str(), newFile.c_str());
+ }
+ }
+ return ret;
+}
+
int copyFile(const char *fromPath, const char *toPath) {
auto start = std::chrono::steady_clock::now();
@@ -96,7 +153,7 @@
PLOG(ERROR) << "Failed to open copy from " << fromPath;
return -1;
}
- android::base::unique_fd toFd(open(toPath, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR));
+ android::base::unique_fd toFd(open(toPath, O_CREAT | O_WRONLY, FILE_PERM));
if (toFd == -1) {
PLOG(ERROR) << "Failed to open copy to " << toPath;
return -1;
@@ -121,23 +178,17 @@
}
auto end = std::chrono::steady_clock::now();
std::chrono::duration<double> diff = end - start;
- LOG(INFO) << "Copied a file with MTP. Time: " << diff.count() << " s, Size: " << length <<
+ LOG(DEBUG) << "Copied a file with MTP. Time: " << diff.count() << " s, Size: " << length <<
", Rate: " << ((double) length) / diff.count() << " bytes/s";
+ chown(toPath, getuid(), FILE_GROUP);
return ret == -1 ? -1 : 0;
}
void deleteRecursive(const char* path) {
- char pathbuf[PATH_MAX];
- size_t pathLength = strlen(path);
- if (pathLength >= sizeof(pathbuf) - 1) {
- LOG(ERROR) << "path too long: " << path;
+ string pathStr(path);
+ if (pathStr[pathStr.size()-1] != '/') {
+ pathStr += '/';
}
- strcpy(pathbuf, path);
- if (pathbuf[pathLength - 1] != '/') {
- pathbuf[pathLength++] = '/';
- }
- char* fileSpot = pathbuf + pathLength;
- int pathRemaining = sizeof(pathbuf) - pathLength - 1;
DIR* dir = opendir(path);
if (!dir) {
@@ -153,19 +204,12 @@
if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0))) {
continue;
}
-
- int nameLength = strlen(name);
- if (nameLength > pathRemaining) {
- LOG(ERROR) << "path " << path << "/" << name << " too long";
- continue;
- }
- strcpy(fileSpot, name);
-
+ pathStr.append(name);
if (entry->d_type == DT_DIR) {
- deleteRecursive(pathbuf);
- rmdir(pathbuf);
+ deleteRecursive(pathStr.c_str());
+ rmdir(pathStr.c_str());
} else {
- unlink(pathbuf);
+ unlink(pathStr.c_str());
}
}
closedir(dir);
diff --git a/media/mtp/MtpUtils.h b/media/mtp/MtpUtils.h
index a2bb7e1..b7c72f5 100644
--- a/media/mtp/MtpUtils.h
+++ b/media/mtp/MtpUtils.h
@@ -17,13 +17,21 @@
#ifndef _MTP_UTILS_H
#define _MTP_UTILS_H
+#include "private/android_filesystem_config.h"
+
#include <stdint.h>
namespace android {
+constexpr int FILE_GROUP = AID_MEDIA_RW;
+constexpr int FILE_PERM = 0664;
+constexpr int DIR_PERM = 0775;
+
bool parseDateTime(const char* dateTime, time_t& outSeconds);
void formatDateTime(time_t seconds, char* buffer, int bufferLength);
+int makeFolder(const char *path);
+int copyRecursive(const char *fromPath, const char *toPath);
int copyFile(const char *fromPath, const char *toPath);
void deleteRecursive(const char* path);
void deletePath(const char* path);