MtpServer.cpp revision 7ce05cf6009a2fbbceb3d2c0ff639473d0b7d6a9
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#include <stdio.h>
18#include <stdlib.h>
19#include <sys/types.h>
20#include <sys/ioctl.h>
21#include <sys/stat.h>
22#include <fcntl.h>
23#include <errno.h>
24#include <sys/stat.h>
25#include <dirent.h>
26
27#include <cutils/properties.h>
28
29#define LOG_TAG "MtpServer"
30
31#include "MtpDebug.h"
32#include "MtpDatabase.h"
33#include "MtpProperty.h"
34#include "MtpServer.h"
35#include "MtpStorage.h"
36#include "MtpStringBuffer.h"
37
38#include <linux/usb/f_mtp.h>
39
40namespace android {
41
42static const MtpOperationCode kSupportedOperationCodes[] = {
43    MTP_OPERATION_GET_DEVICE_INFO,
44    MTP_OPERATION_OPEN_SESSION,
45    MTP_OPERATION_CLOSE_SESSION,
46    MTP_OPERATION_GET_STORAGE_IDS,
47    MTP_OPERATION_GET_STORAGE_INFO,
48    MTP_OPERATION_GET_NUM_OBJECTS,
49    MTP_OPERATION_GET_OBJECT_HANDLES,
50    MTP_OPERATION_GET_OBJECT_INFO,
51    MTP_OPERATION_GET_OBJECT,
52//    MTP_OPERATION_GET_THUMB,
53    MTP_OPERATION_DELETE_OBJECT,
54    MTP_OPERATION_SEND_OBJECT_INFO,
55    MTP_OPERATION_SEND_OBJECT,
56//    MTP_OPERATION_INITIATE_CAPTURE,
57//    MTP_OPERATION_FORMAT_STORE,
58//    MTP_OPERATION_RESET_DEVICE,
59//    MTP_OPERATION_SELF_TEST,
60//    MTP_OPERATION_SET_OBJECT_PROTECTION,
61//    MTP_OPERATION_POWER_DOWN,
62    MTP_OPERATION_GET_DEVICE_PROP_DESC,
63    MTP_OPERATION_GET_DEVICE_PROP_VALUE,
64    MTP_OPERATION_SET_DEVICE_PROP_VALUE,
65    MTP_OPERATION_RESET_DEVICE_PROP_VALUE,
66//    MTP_OPERATION_TERMINATE_OPEN_CAPTURE,
67//    MTP_OPERATION_MOVE_OBJECT,
68//    MTP_OPERATION_COPY_OBJECT,
69//    MTP_OPERATION_GET_PARTIAL_OBJECT,
70//    MTP_OPERATION_INITIATE_OPEN_CAPTURE,
71    MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED,
72    MTP_OPERATION_GET_OBJECT_PROP_DESC,
73    MTP_OPERATION_GET_OBJECT_PROP_VALUE,
74    MTP_OPERATION_SET_OBJECT_PROP_VALUE,
75    MTP_OPERATION_GET_OBJECT_REFERENCES,
76    MTP_OPERATION_SET_OBJECT_REFERENCES,
77//    MTP_OPERATION_SKIP,
78};
79
80static const MtpEventCode kSupportedEventCodes[] = {
81    MTP_EVENT_OBJECT_ADDED,
82    MTP_EVENT_OBJECT_REMOVED,
83};
84
85MtpServer::MtpServer(int fd, MtpDatabase* database,
86                    int fileGroup, int filePerm, int directoryPerm)
87    :   mFD(fd),
88        mDatabase(database),
89        mFileGroup(fileGroup),
90        mFilePermission(filePerm),
91        mDirectoryPermission(directoryPerm),
92        mSessionID(0),
93        mSessionOpen(false),
94        mSendObjectHandle(kInvalidObjectHandle),
95        mSendObjectFormat(0),
96        mSendObjectFileSize(0)
97{
98}
99
100MtpServer::~MtpServer() {
101}
102
103void MtpServer::addStorage(const char* filePath) {
104    int index = mStorages.size() + 1;
105    index |= index << 16;   // set high and low part to our index
106    MtpStorage* storage = new MtpStorage(index, filePath, mDatabase);
107    addStorage(storage);
108}
109
110MtpStorage* MtpServer::getStorage(MtpStorageID id) {
111    for (int i = 0; i < mStorages.size(); i++) {
112        MtpStorage* storage =  mStorages[i];
113        if (storage->getStorageID() == id)
114            return storage;
115    }
116    return NULL;
117}
118
119void MtpServer::run() {
120    int fd = mFD;
121
122    LOGV("MtpServer::run fd: %d\n", fd);
123
124    while (1) {
125        int ret = mRequest.read(fd);
126        if (ret < 0) {
127            LOGE("request read returned %d, errno: %d", ret, errno);
128            if (errno == ECANCELED) {
129                // return to top of loop and wait for next command
130                continue;
131            }
132            break;
133        }
134        MtpOperationCode operation = mRequest.getOperationCode();
135        MtpTransactionID transaction = mRequest.getTransactionID();
136
137        LOGV("operation: %s", MtpDebug::getOperationCodeName(operation));
138        mRequest.dump();
139
140        // FIXME need to generalize this
141        bool dataIn = (operation == MTP_OPERATION_SEND_OBJECT_INFO
142                    || operation == MTP_OPERATION_SET_OBJECT_REFERENCES
143                    || operation == MTP_OPERATION_SET_OBJECT_PROP_VALUE
144                    || operation == MTP_OPERATION_SET_DEVICE_PROP_VALUE);
145        if (dataIn) {
146            int ret = mData.read(fd);
147            if (ret < 0) {
148                LOGE("data read returned %d, errno: %d", ret, errno);
149                if (errno == ECANCELED) {
150                    // return to top of loop and wait for next command
151                    continue;
152                }
153                break;
154            }
155            LOGV("received data:");
156            mData.dump();
157        } else {
158            mData.reset();
159        }
160
161        if (handleRequest()) {
162            if (!dataIn && mData.hasData()) {
163                mData.setOperationCode(operation);
164                mData.setTransactionID(transaction);
165                LOGV("sending data:");
166                mData.dump();
167                ret = mData.write(fd);
168                if (ret < 0) {
169                    LOGE("request write returned %d, errno: %d", ret, errno);
170                    if (errno == ECANCELED) {
171                        // return to top of loop and wait for next command
172                        continue;
173                    }
174                    break;
175                }
176            }
177
178            mResponse.setTransactionID(transaction);
179            LOGV("sending response %04X", mResponse.getResponseCode());
180            ret = mResponse.write(fd);
181            mResponse.dump();
182            if (ret < 0) {
183                LOGE("request write returned %d, errno: %d", ret, errno);
184                if (errno == ECANCELED) {
185                    // return to top of loop and wait for next command
186                    continue;
187                }
188                break;
189            }
190        } else {
191            LOGV("skipping response\n");
192        }
193    }
194
195    if (mSessionOpen)
196        mDatabase->sessionEnded();
197}
198
199void MtpServer::sendObjectAdded(MtpObjectHandle handle) {
200    if (mSessionOpen) {
201        LOGD("sendObjectAdded %d\n", handle);
202        mEvent.setEventCode(MTP_EVENT_OBJECT_ADDED);
203        mEvent.setTransactionID(mRequest.getTransactionID());
204        mEvent.setParameter(1, handle);
205        int ret = mEvent.write(mFD);
206        LOGD("mEvent.write returned %d\n", ret);
207    }
208}
209
210void MtpServer::sendObjectRemoved(MtpObjectHandle handle) {
211    if (mSessionOpen) {
212        LOGD("sendObjectRemoved %d\n", handle);
213        mEvent.setEventCode(MTP_EVENT_OBJECT_REMOVED);
214        mEvent.setTransactionID(mRequest.getTransactionID());
215        mEvent.setParameter(1, handle);
216        int ret = mEvent.write(mFD);
217        LOGD("mEvent.write returned %d\n", ret);
218    }
219}
220
221bool MtpServer::handleRequest() {
222    MtpOperationCode operation = mRequest.getOperationCode();
223    MtpResponseCode response;
224
225    mResponse.reset();
226
227    if (mSendObjectHandle != kInvalidObjectHandle && operation != MTP_OPERATION_SEND_OBJECT) {
228        // FIXME - need to delete mSendObjectHandle from the database
229        LOGE("expected SendObject after SendObjectInfo");
230        mSendObjectHandle = kInvalidObjectHandle;
231    }
232
233    switch (operation) {
234        case MTP_OPERATION_GET_DEVICE_INFO:
235            response = doGetDeviceInfo();
236            break;
237        case MTP_OPERATION_OPEN_SESSION:
238            response = doOpenSession();
239            break;
240        case MTP_OPERATION_CLOSE_SESSION:
241            response = doCloseSession();
242            break;
243        case MTP_OPERATION_GET_STORAGE_IDS:
244            response = doGetStorageIDs();
245            break;
246         case MTP_OPERATION_GET_STORAGE_INFO:
247            response = doGetStorageInfo();
248            break;
249        case MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED:
250            response = doGetObjectPropsSupported();
251            break;
252        case MTP_OPERATION_GET_OBJECT_HANDLES:
253            response = doGetObjectHandles();
254            break;
255        case MTP_OPERATION_GET_NUM_OBJECTS:
256            response = doGetNumObjects();
257            break;
258        case MTP_OPERATION_GET_OBJECT_REFERENCES:
259            response = doGetObjectReferences();
260            break;
261        case MTP_OPERATION_SET_OBJECT_REFERENCES:
262            response = doSetObjectReferences();
263            break;
264        case MTP_OPERATION_GET_OBJECT_PROP_VALUE:
265            response = doGetObjectPropValue();
266            break;
267        case MTP_OPERATION_SET_OBJECT_PROP_VALUE:
268            response = doSetObjectPropValue();
269            break;
270        case MTP_OPERATION_GET_DEVICE_PROP_VALUE:
271            response = doGetDevicePropValue();
272            break;
273        case MTP_OPERATION_SET_DEVICE_PROP_VALUE:
274            response = doSetDevicePropValue();
275            break;
276        case MTP_OPERATION_RESET_DEVICE_PROP_VALUE:
277            response = doResetDevicePropValue();
278            break;
279        case MTP_OPERATION_GET_OBJECT_INFO:
280            response = doGetObjectInfo();
281            break;
282        case MTP_OPERATION_GET_OBJECT:
283            response = doGetObject();
284            break;
285        case MTP_OPERATION_SEND_OBJECT_INFO:
286            response = doSendObjectInfo();
287            break;
288        case MTP_OPERATION_SEND_OBJECT:
289            response = doSendObject();
290            break;
291        case MTP_OPERATION_DELETE_OBJECT:
292            response = doDeleteObject();
293            break;
294        case MTP_OPERATION_GET_OBJECT_PROP_DESC:
295            response = doGetObjectPropDesc();
296            break;
297        case MTP_OPERATION_GET_DEVICE_PROP_DESC:
298            response = doGetDevicePropDesc();
299            break;
300        default:
301            LOGE("got unsupported command %s", MtpDebug::getOperationCodeName(operation));
302            response = MTP_RESPONSE_OPERATION_NOT_SUPPORTED;
303            break;
304    }
305
306    if (response == MTP_RESPONSE_TRANSACTION_CANCELLED)
307        return false;
308    mResponse.setResponseCode(response);
309    return true;
310}
311
312MtpResponseCode MtpServer::doGetDeviceInfo() {
313    MtpStringBuffer   string;
314    char prop_value[PROPERTY_VALUE_MAX];
315
316    MtpObjectFormatList* playbackFormats = mDatabase->getSupportedPlaybackFormats();
317    MtpObjectFormatList* captureFormats = mDatabase->getSupportedCaptureFormats();
318    MtpDevicePropertyList* deviceProperties = mDatabase->getSupportedDeviceProperties();
319
320    // fill in device info
321    mData.putUInt16(MTP_STANDARD_VERSION);
322    mData.putUInt32(6); // MTP Vendor Extension ID
323    mData.putUInt16(MTP_STANDARD_VERSION);
324    string.set("microsoft.com: 1.0;");
325    mData.putString(string); // MTP Extensions
326    mData.putUInt16(0); //Functional Mode
327    mData.putAUInt16(kSupportedOperationCodes,
328            sizeof(kSupportedOperationCodes) / sizeof(uint16_t)); // Operations Supported
329    mData.putAUInt16(kSupportedEventCodes,
330            sizeof(kSupportedEventCodes) / sizeof(uint16_t)); // Events Supported
331    mData.putAUInt16(deviceProperties); // Device Properties Supported
332    mData.putAUInt16(captureFormats); // Capture Formats
333    mData.putAUInt16(playbackFormats);  // Playback Formats
334    // FIXME
335    string.set("Google, Inc.");
336    mData.putString(string);   // Manufacturer
337
338    property_get("ro.product.model", prop_value, "MTP Device");
339    string.set(prop_value);
340    mData.putString(string);   // Model
341    string.set("1.0");
342    mData.putString(string);   // Device Version
343
344    property_get("ro.serialno", prop_value, "????????");
345    string.set(prop_value);
346    mData.putString(string);   // Serial Number
347
348    delete playbackFormats;
349    delete captureFormats;
350    delete deviceProperties;
351
352    return MTP_RESPONSE_OK;
353}
354
355MtpResponseCode MtpServer::doOpenSession() {
356    if (mSessionOpen) {
357        mResponse.setParameter(1, mSessionID);
358        return MTP_RESPONSE_SESSION_ALREADY_OPEN;
359    }
360    mSessionID = mRequest.getParameter(1);
361    mSessionOpen = true;
362
363    mDatabase->sessionStarted();
364
365    return MTP_RESPONSE_OK;
366}
367
368MtpResponseCode MtpServer::doCloseSession() {
369    if (!mSessionOpen)
370        return MTP_RESPONSE_SESSION_NOT_OPEN;
371    mSessionID = 0;
372    mSessionOpen = false;
373    mDatabase->sessionEnded();
374    return MTP_RESPONSE_OK;
375}
376
377MtpResponseCode MtpServer::doGetStorageIDs() {
378    if (!mSessionOpen)
379        return MTP_RESPONSE_SESSION_NOT_OPEN;
380
381    int count = mStorages.size();
382    mData.putUInt32(count);
383    for (int i = 0; i < count; i++)
384        mData.putUInt32(mStorages[i]->getStorageID());
385
386    return MTP_RESPONSE_OK;
387}
388
389MtpResponseCode MtpServer::doGetStorageInfo() {
390    MtpStringBuffer   string;
391
392    if (!mSessionOpen)
393        return MTP_RESPONSE_SESSION_NOT_OPEN;
394    MtpStorageID id = mRequest.getParameter(1);
395    MtpStorage* storage = getStorage(id);
396    if (!storage)
397        return MTP_RESPONSE_INVALID_STORAGE_ID;
398
399    mData.putUInt16(storage->getType());
400    mData.putUInt16(storage->getFileSystemType());
401    mData.putUInt16(storage->getAccessCapability());
402    mData.putUInt64(storage->getMaxCapacity());
403    mData.putUInt64(storage->getFreeSpace());
404    mData.putUInt32(1024*1024*1024); // Free Space in Objects
405    string.set(storage->getDescription());
406    mData.putString(string);
407    mData.putEmptyString();   // Volume Identifier
408
409    return MTP_RESPONSE_OK;
410}
411
412MtpResponseCode MtpServer::doGetObjectPropsSupported() {
413    if (!mSessionOpen)
414        return MTP_RESPONSE_SESSION_NOT_OPEN;
415    MtpObjectFormat format = mRequest.getParameter(1);
416    MtpDevicePropertyList* properties = mDatabase->getSupportedObjectProperties(format);
417    mData.putAUInt16(properties);
418    delete properties;
419    return MTP_RESPONSE_OK;
420}
421
422MtpResponseCode MtpServer::doGetObjectHandles() {
423    if (!mSessionOpen)
424        return MTP_RESPONSE_SESSION_NOT_OPEN;
425    MtpStorageID storageID = mRequest.getParameter(1);      // 0xFFFFFFFF for all storage
426    MtpObjectFormat format = mRequest.getParameter(2);      // 0 for all formats
427    MtpObjectHandle parent = mRequest.getParameter(3);      // 0xFFFFFFFF for objects with no parent
428                                                            // 0x00000000 for all objects?
429    if (parent == 0xFFFFFFFF)
430        parent = 0;
431
432    MtpObjectHandleList* handles = mDatabase->getObjectList(storageID, format, parent);
433    mData.putAUInt32(handles);
434    delete handles;
435    return MTP_RESPONSE_OK;
436}
437
438MtpResponseCode MtpServer::doGetNumObjects() {
439    if (!mSessionOpen)
440        return MTP_RESPONSE_SESSION_NOT_OPEN;
441    MtpStorageID storageID = mRequest.getParameter(1);      // 0xFFFFFFFF for all storage
442    MtpObjectFormat format = mRequest.getParameter(2);      // 0 for all formats
443    MtpObjectHandle parent = mRequest.getParameter(3);      // 0xFFFFFFFF for objects with no parent
444                                                            // 0x00000000 for all objects?
445    if (parent == 0xFFFFFFFF)
446        parent = 0;
447
448    int count = mDatabase->getNumObjects(storageID, format, parent);
449    if (count >= 0) {
450        mResponse.setParameter(1, count);
451        return MTP_RESPONSE_OK;
452    } else {
453        mResponse.setParameter(1, 0);
454        return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
455    }
456}
457
458MtpResponseCode MtpServer::doGetObjectReferences() {
459    if (!mSessionOpen)
460        return MTP_RESPONSE_SESSION_NOT_OPEN;
461    MtpStorageID handle = mRequest.getParameter(1);
462
463    // FIXME - check for invalid object handle
464    MtpObjectHandleList* handles = mDatabase->getObjectReferences(handle);
465    if (handles) {
466        mData.putAUInt32(handles);
467        delete handles;
468    } else {
469        mData.putEmptyArray();
470    }
471    return MTP_RESPONSE_OK;
472}
473
474MtpResponseCode MtpServer::doSetObjectReferences() {
475    if (!mSessionOpen)
476        return MTP_RESPONSE_SESSION_NOT_OPEN;
477    MtpStorageID handle = mRequest.getParameter(1);
478    MtpObjectHandleList* references = mData.getAUInt32();
479    MtpResponseCode result = mDatabase->setObjectReferences(handle, references);
480    delete references;
481    return result;
482}
483
484MtpResponseCode MtpServer::doGetObjectPropValue() {
485    MtpObjectHandle handle = mRequest.getParameter(1);
486    MtpObjectProperty property = mRequest.getParameter(2);
487    LOGD("GetObjectPropValue %d %s\n", handle,
488            MtpDebug::getObjectPropCodeName(property));
489
490    return mDatabase->getObjectPropertyValue(handle, property, mData);
491}
492
493MtpResponseCode MtpServer::doSetObjectPropValue() {
494    MtpObjectHandle handle = mRequest.getParameter(1);
495    MtpObjectProperty property = mRequest.getParameter(2);
496    LOGD("SetObjectPropValue %d %s\n", handle,
497            MtpDebug::getObjectPropCodeName(property));
498
499    return mDatabase->setObjectPropertyValue(handle, property, mData);
500}
501
502MtpResponseCode MtpServer::doGetDevicePropValue() {
503    MtpDeviceProperty property = mRequest.getParameter(1);
504    LOGD("GetDevicePropValue %s\n",
505            MtpDebug::getDevicePropCodeName(property));
506
507    return mDatabase->getDevicePropertyValue(property, mData);
508}
509
510MtpResponseCode MtpServer::doSetDevicePropValue() {
511    MtpDeviceProperty property = mRequest.getParameter(1);
512    LOGD("SetDevicePropValue %s\n",
513            MtpDebug::getDevicePropCodeName(property));
514
515    return mDatabase->setDevicePropertyValue(property, mData);
516}
517
518MtpResponseCode MtpServer::doResetDevicePropValue() {
519    MtpDeviceProperty property = mRequest.getParameter(1);
520    LOGD("ResetDevicePropValue %s\n",
521            MtpDebug::getDevicePropCodeName(property));
522
523    return mDatabase->resetDeviceProperty(property);
524}
525
526MtpResponseCode MtpServer::doGetObjectInfo() {
527    MtpObjectHandle handle = mRequest.getParameter(1);
528    return mDatabase->getObjectInfo(handle, mData);
529}
530
531MtpResponseCode MtpServer::doGetObject() {
532    MtpObjectHandle handle = mRequest.getParameter(1);
533    MtpString pathBuf;
534    int64_t fileLength;
535    int result = mDatabase->getObjectFilePath(handle, pathBuf, fileLength);
536    if (result != MTP_RESPONSE_OK)
537        return result;
538
539    const char* filePath = (const char *)pathBuf;
540    mtp_file_range  mfr;
541    mfr.fd = open(filePath, O_RDONLY);
542    if (mfr.fd < 0) {
543        return MTP_RESPONSE_GENERAL_ERROR;
544    }
545    mfr.offset = 0;
546    mfr.length = fileLength;
547
548    // send data header
549    mData.setOperationCode(mRequest.getOperationCode());
550    mData.setTransactionID(mRequest.getTransactionID());
551    mData.writeDataHeader(mFD, fileLength + MTP_CONTAINER_HEADER_SIZE);
552
553    // then transfer the file
554    int ret = ioctl(mFD, MTP_SEND_FILE, (unsigned long)&mfr);
555    close(mfr.fd);
556    if (ret < 0) {
557        if (errno == ECANCELED)
558            return MTP_RESPONSE_TRANSACTION_CANCELLED;
559        else
560            return MTP_RESPONSE_GENERAL_ERROR;
561    }
562    return MTP_RESPONSE_OK;
563}
564
565MtpResponseCode MtpServer::doSendObjectInfo() {
566    MtpString path;
567    MtpStorageID storageID = mRequest.getParameter(1);
568    MtpStorage* storage = getStorage(storageID);
569    MtpObjectHandle parent = mRequest.getParameter(2);
570    if (!storage)
571        return MTP_RESPONSE_INVALID_STORAGE_ID;
572
573    // special case the root
574    if (parent == MTP_PARENT_ROOT) {
575        path = storage->getPath();
576        parent = 0;
577    } else {
578        int64_t dummy;
579        int result = mDatabase->getObjectFilePath(parent, path, dummy);
580        if (result != MTP_RESPONSE_OK)
581            return result;
582    }
583
584    // read only the fields we need
585    mData.getUInt32();  // storage ID
586    MtpObjectFormat format = mData.getUInt16();
587    mData.getUInt16();  // protection status
588    mSendObjectFileSize = mData.getUInt32();
589    mData.getUInt16();  // thumb format
590    mData.getUInt32();  // thumb compressed size
591    mData.getUInt32();  // thumb pix width
592    mData.getUInt32();  // thumb pix height
593    mData.getUInt32();  // image pix width
594    mData.getUInt32();  // image pix height
595    mData.getUInt32();  // image bit depth
596    mData.getUInt32();  // parent
597    uint16_t associationType = mData.getUInt16();
598    uint32_t associationDesc = mData.getUInt32();   // association desc
599    mData.getUInt32();  // sequence number
600    MtpStringBuffer name, created, modified;
601    mData.getString(name);    // file name
602    mData.getString(created);      // date created
603    mData.getString(modified);     // date modified
604    // keywords follow
605
606    time_t modifiedTime;
607    if (!parseDateTime(modified, modifiedTime))
608        modifiedTime = 0;
609
610    if (path[path.size() - 1] != '/')
611        path += "/";
612    path += (const char *)name;
613
614    MtpObjectHandle handle = mDatabase->beginSendObject((const char*)path,
615            format, parent, storageID, mSendObjectFileSize, modifiedTime);
616    if (handle == kInvalidObjectHandle) {
617        return MTP_RESPONSE_GENERAL_ERROR;
618    }
619
620  if (format == MTP_FORMAT_ASSOCIATION) {
621        mode_t mask = umask(0);
622        int ret = mkdir((const char *)path, mDirectoryPermission);
623        umask(mask);
624        if (ret && ret != -EEXIST)
625            return MTP_RESPONSE_GENERAL_ERROR;
626        chown((const char *)path, getuid(), mFileGroup);
627    } else {
628        mSendObjectFilePath = path;
629        // save the handle for the SendObject call, which should follow
630        mSendObjectHandle = handle;
631        mSendObjectFormat = format;
632    }
633
634    mResponse.setParameter(1, storageID);
635    mResponse.setParameter(2, parent);
636    mResponse.setParameter(3, handle);
637
638    return MTP_RESPONSE_OK;
639}
640
641MtpResponseCode MtpServer::doSendObject() {
642    MtpResponseCode result = MTP_RESPONSE_OK;
643    mode_t mask;
644    int ret;
645
646    if (mSendObjectHandle == kInvalidObjectHandle) {
647        LOGE("Expected SendObjectInfo before SendObject");
648        result = MTP_RESPONSE_NO_VALID_OBJECT_INFO;
649        goto done;
650    }
651
652    // read the header
653    ret = mData.readDataHeader(mFD);
654    // FIXME - check for errors here.
655
656    // reset so we don't attempt to send this back
657    mData.reset();
658
659    mtp_file_range  mfr;
660    mfr.fd = open(mSendObjectFilePath, O_RDWR | O_CREAT | O_TRUNC);
661    if (mfr.fd < 0) {
662        result = MTP_RESPONSE_GENERAL_ERROR;
663        goto done;
664    }
665    fchown(mfr.fd, getuid(), mFileGroup);
666    // set permissions
667    mask = umask(0);
668    fchmod(mfr.fd, mFilePermission);
669    umask(mask);
670
671    mfr.offset = 0;
672    mfr.length = mSendObjectFileSize;
673
674    // transfer the file
675    ret = ioctl(mFD, MTP_RECEIVE_FILE, (unsigned long)&mfr);
676    close(mfr.fd);
677
678    LOGV("MTP_RECEIVE_FILE returned %d", ret);
679
680    if (ret < 0) {
681        unlink(mSendObjectFilePath);
682        if (errno == ECANCELED)
683            result = MTP_RESPONSE_TRANSACTION_CANCELLED;
684        else
685            result = MTP_RESPONSE_GENERAL_ERROR;
686    }
687
688done:
689    mDatabase->endSendObject(mSendObjectFilePath, mSendObjectHandle, mSendObjectFormat,
690            result == MTP_RESPONSE_OK);
691    mSendObjectHandle = kInvalidObjectHandle;
692    mSendObjectFormat = 0;
693    return result;
694}
695
696static void deleteRecursive(const char* path) {
697    char pathbuf[PATH_MAX];
698    int pathLength = strlen(path);
699    if (pathLength >= sizeof(pathbuf) - 1) {
700        LOGE("path too long: %s\n", path);
701    }
702    strcpy(pathbuf, path);
703    if (pathbuf[pathLength - 1] != '/') {
704        pathbuf[pathLength++] = '/';
705    }
706    char* fileSpot = pathbuf + pathLength;
707    int pathRemaining = sizeof(pathbuf) - pathLength - 1;
708
709    DIR* dir = opendir(path);
710    if (!dir) {
711        LOGE("opendir %s failed: %s", path, strerror(errno));
712        return;
713    }
714
715    struct dirent* entry;
716    while ((entry = readdir(dir))) {
717        const char* name = entry->d_name;
718
719        // ignore "." and ".."
720        if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0))) {
721            continue;
722        }
723
724        int nameLength = strlen(name);
725        if (nameLength > pathRemaining) {
726            LOGE("path %s/%s too long\n", path, name);
727            continue;
728        }
729        strcpy(fileSpot, name);
730
731        int type = entry->d_type;
732        if (entry->d_type == DT_DIR) {
733            deleteRecursive(pathbuf);
734            rmdir(pathbuf);
735        } else {
736            unlink(pathbuf);
737        }
738    }
739    closedir(dir);
740}
741
742static void deletePath(const char* path) {
743    struct stat statbuf;
744    if (stat(path, &statbuf) == 0) {
745        if (S_ISDIR(statbuf.st_mode)) {
746            deleteRecursive(path);
747            rmdir(path);
748        } else {
749            unlink(path);
750        }
751    } else {
752        LOGE("deletePath stat failed for %s: %s", path, strerror(errno));
753    }
754}
755
756MtpResponseCode MtpServer::doDeleteObject() {
757    MtpObjectHandle handle = mRequest.getParameter(1);
758    MtpObjectFormat format = mRequest.getParameter(2);
759    // FIXME - support deleting all objects if handle is 0xFFFFFFFF
760    // FIXME - implement deleting objects by format
761
762    MtpString filePath;
763    int64_t fileLength;
764    int result = mDatabase->getObjectFilePath(handle, filePath, fileLength);
765    if (result == MTP_RESPONSE_OK) {
766        LOGV("deleting %s", (const char *)filePath);
767        deletePath((const char *)filePath);
768        return mDatabase->deleteFile(handle);
769    } else {
770        return result;
771    }
772}
773
774MtpResponseCode MtpServer::doGetObjectPropDesc() {
775    MtpObjectProperty propCode = mRequest.getParameter(1);
776    MtpObjectFormat format = mRequest.getParameter(2);
777    LOGD("GetObjectPropDesc %s %s\n", MtpDebug::getObjectPropCodeName(propCode),
778                                        MtpDebug::getFormatCodeName(format));
779    MtpProperty* property = mDatabase->getObjectPropertyDesc(propCode, format);
780    if (!property)
781        return MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED;
782    property->write(mData);
783    delete property;
784    return MTP_RESPONSE_OK;
785}
786
787MtpResponseCode MtpServer::doGetDevicePropDesc() {
788    MtpDeviceProperty propCode = mRequest.getParameter(1);
789    LOGD("GetDevicePropDesc %s\n", MtpDebug::getDevicePropCodeName(propCode));
790    MtpProperty* property = mDatabase->getDevicePropertyDesc(propCode);
791    if (!property)
792        return MTP_RESPONSE_DEVICE_PROP_NOT_SUPPORTED;
793    property->write(mData);
794    delete property;
795    return MTP_RESPONSE_OK;
796}
797
798}  // namespace android
799