mtp

Merge lp:~phablet-team/mtp/aosp-update into lp:mtp

Proposed by Alfonso Sanchez-Beato
Status: Merged
Approved by: Konrad Zapałowicz
Approved revision: 74
Merged at revision: 70
Proposed branch: lp:~phablet-team/mtp/aosp-update
Merge into: lp:mtp
Diff against target: 2258 lines (+634/-357)
25 files modified
CMakeLists.txt (+1/-1)
include/MtpDataPacket.h (+13/-12)
include/MtpDevice.h (+1/-1)
include/MtpDeviceInfo.h (+2/-2)
include/MtpObjectInfo.h (+1/-1)
include/MtpPacket.h (+4/-4)
include/MtpProperty.h (+7/-7)
include/MtpRequestPacket.h (+4/-0)
include/MtpServer.h (+1/-0)
include/MtpStorageInfo.h (+1/-1)
include/MtpStringBuffer.h (+5/-2)
server/UbuntuMtpDatabase.h (+48/-37)
server/server.cpp (+25/-23)
src/MtpDataPacket.cpp (+129/-55)
src/MtpDevice.cpp (+33/-14)
src/MtpDeviceInfo.cpp (+19/-12)
src/MtpObjectInfo.cpp (+22/-20)
src/MtpPacket.cpp (+1/-1)
src/MtpProperty.cpp (+63/-42)
src/MtpRequestPacket.cpp (+17/-4)
src/MtpServer.cpp (+170/-64)
src/MtpStorageInfo.cpp (+11/-9)
src/MtpStringBuffer.cpp (+36/-24)
src/MtpUtils.cpp (+19/-20)
tests/TestMtpUtils.cpp (+1/-1)
To merge this branch: bzr merge lp:~phablet-team/mtp/aosp-update
Reviewer Review Type Date Requested Status
Konrad Zapałowicz (community) code Approve
Simon Fels Approve
Review via email: mp+291626@code.launchpad.net

Commit message

* Update to latest AOSP code (Marshallow)
* Make MTP work for file > 4GB (LP: #1472789). This involved adding O_LARGEFILE flag when opening a file (the flag is automatically added by bionic, but not by glibc) and fixing parts of the MTP database implementation.

Description of the change

* Update to latest AOSP code (Marshallow)
* Make MTP work for file > 4GB (LP: #1472789). This involved adding O_LARGEFILE flag when opening a file (the flag is automatically added by bionic, but not by glibc) and fixing parts of the MTP database implementation.

To post a comment you must log in.
Revision history for this message
Konrad Zapałowicz (kzapalowicz) wrote :

Minor issues mostly related to type-safety

review: Needs Fixing (code)
Revision history for this message
Alfonso Sanchez-Beato (alfonsosanchezbeato) wrote :

Thanks for the thorough review. However, most of the code comes directly from AOSP, and I have preferred in many cases to minimize the difference between our project and "upstream" to ease future merges. AOSP code can be found in:

https://android.googlesource.com/platform/frameworks/av/

lp:~phablet-team/mtp/aosp-update updated
74. By Alfonso Sanchez-Beato

Address review comments

Revision history for this message
Alfonso Sanchez-Beato (alfonsosanchezbeato) wrote :

Branch refreshed after addressing review comments.

Revision history for this message
Simon Fels (morphis) wrote :

LGTM

review: Approve
Revision history for this message
Konrad Zapałowicz (kzapalowicz) wrote :

Approved

review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'CMakeLists.txt'
2--- CMakeLists.txt 2014-09-04 01:11:14 +0000
3+++ CMakeLists.txt 2016-04-12 16:15:02 +0000
4@@ -7,7 +7,7 @@
5
6 set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
7
8-add_definitions(-DMTP_DEVICE -DMTP_HOST)
9+add_definitions(-DMTP_DEVICE -DMTP_HOST -D_FILE_OFFSET_BITS=64)
10
11 set(MTP_VERSION_MAJOR 1)
12 set(MTP_VERSION_MINOR 0)
13
14=== modified file 'include/MtpDataPacket.h'
15--- include/MtpDataPacket.h 2013-06-13 10:22:21 +0000
16+++ include/MtpDataPacket.h 2016-04-12 16:15:02 +0000
17@@ -30,7 +30,7 @@
18 class MtpDataPacket : public MtpPacket {
19 private:
20 // current offset for get/put methods
21- int mOffset;
22+ size_t mOffset;
23
24 public:
25 MtpDataPacket();
26@@ -42,17 +42,18 @@
27 void setTransactionID(MtpTransactionID id);
28
29 inline const uint8_t* getData() const { return mBuffer + MTP_CONTAINER_HEADER_SIZE; }
30- inline uint8_t getUInt8() { return (uint8_t)mBuffer[mOffset++]; }
31- inline int8_t getInt8() { return (int8_t)mBuffer[mOffset++]; }
32- uint16_t getUInt16();
33- inline int16_t getInt16() { return (int16_t)getUInt16(); }
34- uint32_t getUInt32();
35- inline int32_t getInt32() { return (int32_t)getUInt32(); }
36- uint64_t getUInt64();
37- inline int64_t getInt64() { return (int64_t)getUInt64(); }
38- void getUInt128(uint128_t& value);
39- inline void getInt128(int128_t& value) { getUInt128((uint128_t&)value); }
40- void getString(MtpStringBuffer& string);
41+
42+ bool getUInt8(uint8_t& value);
43+ inline bool getInt8(int8_t& value) { return getUInt8((uint8_t&)value); }
44+ bool getUInt16(uint16_t& value);
45+ inline bool getInt16(int16_t& value) { return getUInt16((uint16_t&)value); }
46+ bool getUInt32(uint32_t& value);
47+ inline bool getInt32(int32_t& value) { return getUInt32((uint32_t&)value); }
48+ bool getUInt64(uint64_t& value);
49+ inline bool getInt64(int64_t& value) { return getUInt64((uint64_t&)value); }
50+ bool getUInt128(uint128_t& value);
51+ inline bool getInt128(int128_t& value) { return getUInt128((uint128_t&)value); }
52+ bool getString(MtpStringBuffer& string);
53
54 Int8List* getAInt8();
55 UInt8List* getAUInt8();
56
57=== modified file 'include/MtpDevice.h'
58--- include/MtpDevice.h 2013-06-13 10:22:21 +0000
59+++ include/MtpDevice.h 2016-04-12 16:15:02 +0000
60@@ -96,7 +96,7 @@
61 bool readObject(MtpObjectHandle handle,
62 bool (* callback)(void* data, int offset,
63 int length, void* clientData),
64- int objectSize, void* clientData);
65+ size_t objectSize, void* clientData);
66 bool readObject(MtpObjectHandle handle, const char* destPath, int group,
67 int perm);
68
69
70=== modified file 'include/MtpDeviceInfo.h'
71--- include/MtpDeviceInfo.h 2013-06-13 10:22:21 +0000
72+++ include/MtpDeviceInfo.h 2016-04-12 16:15:02 +0000
73@@ -29,7 +29,7 @@
74 uint32_t mVendorExtensionID;
75 uint16_t mVendorExtensionVersion;
76 char* mVendorExtensionDesc;
77- uint16_t mFunctionalCode;
78+ uint16_t mFunctionalMode;
79 UInt16List* mOperations;
80 UInt16List* mEvents;
81 MtpDevicePropertyList* mDeviceProperties;
82@@ -44,7 +44,7 @@
83 MtpDeviceInfo();
84 virtual ~MtpDeviceInfo();
85
86- void read(MtpDataPacket& packet);
87+ bool read(MtpDataPacket& packet);
88
89 void print();
90 };
91
92=== modified file 'include/MtpObjectInfo.h'
93--- include/MtpObjectInfo.h 2013-06-13 10:22:21 +0000
94+++ include/MtpObjectInfo.h 2016-04-12 16:15:02 +0000
95@@ -50,7 +50,7 @@
96 MtpObjectInfo(MtpObjectHandle handle);
97 virtual ~MtpObjectInfo();
98
99- void read(MtpDataPacket& packet);
100+ bool read(MtpDataPacket& packet);
101
102 void print();
103 };
104
105=== modified file 'include/MtpPacket.h'
106--- include/MtpPacket.h 2013-06-13 10:22:21 +0000
107+++ include/MtpPacket.h 2016-04-12 16:15:02 +0000
108@@ -28,11 +28,11 @@
109 protected:
110 uint8_t* mBuffer;
111 // current size of the buffer
112- int mBufferSize;
113+ size_t mBufferSize;
114 // number of bytes to add when resizing the buffer
115- int mAllocationIncrement;
116+ size_t mAllocationIncrement;
117 // size of the data in the packet
118- int mPacketSize;
119+ size_t mPacketSize;
120
121 public:
122 MtpPacket(int bufferSize);
123@@ -41,7 +41,7 @@
124 // sets packet size to the default container size and sets buffer to zero
125 virtual void reset();
126
127- void allocate(int length);
128+ void allocate(size_t length);
129 void dump();
130 void copyFrom(const MtpPacket& src);
131
132
133=== modified file 'include/MtpProperty.h'
134--- include/MtpProperty.h 2013-06-13 10:22:21 +0000
135+++ include/MtpProperty.h 2016-04-12 16:15:02 +0000
136@@ -49,9 +49,9 @@
137 MtpPropertyValue mCurrentValue;
138
139 // for array types
140- int mDefaultArrayLength;
141+ uint32_t mDefaultArrayLength;
142 MtpPropertyValue* mDefaultArrayValues;
143- int mCurrentArrayLength;
144+ uint32_t mCurrentArrayLength;
145 MtpPropertyValue* mCurrentArrayValues;
146
147 enum {
148@@ -70,7 +70,7 @@
149 MtpPropertyValue mStepSize;
150
151 // for enum form
152- int mEnumLength;
153+ uint16_t mEnumLength;
154 MtpPropertyValue* mEnumValues;
155
156 public:
157@@ -83,7 +83,7 @@
158
159 inline MtpPropertyCode getPropertyCode() const { return mCode; }
160
161- void read(MtpDataPacket& packet);
162+ bool read(MtpDataPacket& packet);
163 void write(MtpDataPacket& packet);
164
165 void setDefaultValue(const uint16_t* string);
166@@ -102,11 +102,11 @@
167 }
168
169 private:
170- void readValue(MtpDataPacket& packet, MtpPropertyValue& value);
171+ bool readValue(MtpDataPacket& packet, MtpPropertyValue& value);
172 void writeValue(MtpDataPacket& packet, MtpPropertyValue& value);
173- MtpPropertyValue* readArrayValues(MtpDataPacket& packet, int& length);
174+ MtpPropertyValue* readArrayValues(MtpDataPacket& packet, uint32_t& length);
175 void writeArrayValues(MtpDataPacket& packet,
176- MtpPropertyValue* values, int length);
177+ MtpPropertyValue* values, uint32_t length);
178 };
179
180 }; // namespace android
181
182=== modified file 'include/MtpRequestPacket.h'
183--- include/MtpRequestPacket.h 2013-06-13 10:22:21 +0000
184+++ include/MtpRequestPacket.h 2016-04-12 16:15:02 +0000
185@@ -43,6 +43,10 @@
186 inline MtpOperationCode getOperationCode() const { return getContainerCode(); }
187 inline void setOperationCode(MtpOperationCode code)
188 { return setContainerCode(code); }
189+ inline int getParameterCount() const { return mParameterCount; }
190+
191+private:
192+ int mParameterCount;
193 };
194
195 }; // namespace android
196
197=== modified file 'include/MtpServer.h'
198--- include/MtpServer.h 2014-10-29 18:46:35 +0000
199+++ include/MtpServer.h 2016-04-12 16:15:02 +0000
200@@ -108,6 +108,7 @@
201
202 void sendObjectAdded(MtpObjectHandle handle);
203 void sendObjectRemoved(MtpObjectHandle handle);
204+ void sendDevicePropertyChanged(MtpDeviceProperty property);
205 void sendObjectInfoChanged(MtpObjectHandle handle);
206 void sendObjectPropChanged(MtpObjectHandle handle,
207 MtpObjectProperty prop);
208
209=== modified file 'include/MtpStorageInfo.h'
210--- include/MtpStorageInfo.h 2013-06-13 10:22:21 +0000
211+++ include/MtpStorageInfo.h 2016-04-12 16:15:02 +0000
212@@ -39,7 +39,7 @@
213 MtpStorageInfo(MtpStorageID id);
214 virtual ~MtpStorageInfo();
215
216- void read(MtpDataPacket& packet);
217+ bool read(MtpDataPacket& packet);
218
219 void print();
220 };
221
222=== modified file 'include/MtpStringBuffer.h'
223--- include/MtpStringBuffer.h 2013-06-13 10:22:21 +0000
224+++ include/MtpStringBuffer.h 2016-04-12 16:15:02 +0000
225@@ -19,6 +19,9 @@
226
227 #include <stdint.h>
228
229+// Max Character number of a MTP String
230+#define MTP_STRING_MAX_CHARACTER_NUMBER 255
231+
232 namespace android {
233
234 class MtpDataPacket;
235@@ -29,7 +32,7 @@
236 private:
237 // mBuffer contains string in UTF8 format
238 // maximum 3 bytes/character, with 1 extra for zero termination
239- uint8_t mBuffer[255 * 3 + 1];
240+ uint8_t mBuffer[MTP_STRING_MAX_CHARACTER_NUMBER * 3 + 1];
241 int mCharCount;
242 int mByteCount;
243
244@@ -43,7 +46,7 @@
245 void set(const char* src);
246 void set(const uint16_t* src);
247
248- void readFromPacket(MtpDataPacket* packet);
249+ bool readFromPacket(MtpDataPacket* packet);
250 void writeToPacket(MtpDataPacket* packet) const;
251
252 inline int getCharCount() const { return mCharCount; }
253
254=== modified file 'server/UbuntuMtpDatabase.h'
255--- server/UbuntuMtpDatabase.h 2015-01-26 19:35:23 +0000
256+++ server/UbuntuMtpDatabase.h 2016-04-12 16:15:02 +0000
257@@ -63,7 +63,7 @@
258 MtpStorageID storage_id;
259 MtpObjectFormat object_format;
260 MtpObjectHandle parent;
261- size_t object_size;
262+ uint64_t object_size;
263 std::string display_name;
264 std::string path;
265 int watch_fd;
266@@ -117,7 +117,7 @@
267 IN_MODIFY | IN_CREATE | IN_DELETE);
268 }
269
270-
271+
272 void add_file_entry(path p, MtpObjectHandle parent, MtpStorageID storage)
273 {
274 MtpObjectHandle handle = counter;
275@@ -246,16 +246,16 @@
276 std::size_t transferred)
277 {
278 size_t processed = 0;
279-
280+
281 while(transferred - processed >= sizeof(inotify_event))
282 {
283 const char* cdata = processed + asio::buffer_cast<const char*>(buf.data());
284 const inotify_event* ievent = reinterpret_cast<const inotify_event*>(cdata);
285 MtpObjectHandle parent;
286 path p;
287-
288+
289 processed += sizeof(inotify_event) + ievent->len;
290-
291+
292 BOOST_FOREACH(MtpObjectHandle i, db | boost::adaptors::map_keys) {
293 if (db.at(i).watch_fd == ievent->wd) {
294 parent = i;
295@@ -329,7 +329,7 @@
296 }
297 }
298 }
299-
300+
301 read_more_notify();
302 }
303
304@@ -411,7 +411,7 @@
305
306 counter++;
307
308- return handle;
309+ return handle;
310 }
311
312 // called to report success or failure of the SendObject file transfer
313@@ -470,7 +470,7 @@
314 {
315 list = new MtpObjectHandleList();
316 }
317-
318+
319 return list;
320 }
321
322@@ -491,7 +491,7 @@
323 } catch(...)
324 {
325 }
326-
327+
328 return result;
329 }
330
331@@ -540,14 +540,14 @@
332
333 return new MtpObjectFormatList{list};
334 }
335-
336+
337 virtual MtpObjectFormatList* getSupportedCaptureFormats()
338 {
339 VLOG(1) << __PRETTY_FUNCTION__;
340 static const MtpObjectFormatList list = {MTP_FORMAT_ASSOCIATION, MTP_FORMAT_PNG};
341 return new MtpObjectFormatList{list};
342 }
343-
344+
345 virtual MtpObjectPropertyList* getSupportedObjectProperties(MtpObjectFormat format)
346 {
347 VLOG(1) << __PRETTY_FUNCTION__;
348@@ -555,8 +555,8 @@
349 if (format != MTP_FORMAT_PNG)
350 return nullptr;
351 */
352-
353- static const MtpObjectPropertyList list =
354+
355+ static const MtpObjectPropertyList list =
356 {
357 MTP_PROPERTY_STORAGE_ID,
358 MTP_PROPERTY_PARENT_OBJECT,
359@@ -574,16 +574,16 @@
360 MTP_PROPERTY_NON_CONSUMABLE,
361
362 };
363-
364+
365 return new MtpObjectPropertyList{list};
366 }
367-
368+
369 virtual MtpDevicePropertyList* getSupportedDeviceProperties()
370 {
371 VLOG(1) << __PRETTY_FUNCTION__;
372 static const MtpDevicePropertyList list = {
373 MTP_DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME,
374- MTP_DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER,
375+ MTP_DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER,
376 };
377 return new MtpDevicePropertyList{list};
378 }
379@@ -592,7 +592,7 @@
380 MtpObjectHandle handle,
381 MtpObjectProperty property,
382 MtpDataPacket& packet)
383- {
384+ {
385 char date[20];
386
387 VLOG(1) << __PRETTY_FUNCTION__
388@@ -605,10 +605,10 @@
389 try {
390 switch(property)
391 {
392- case MTP_PROPERTY_STORAGE_ID: packet.putUInt32(db.at(handle).storage_id); break;
393- case MTP_PROPERTY_PARENT_OBJECT: packet.putUInt32(db.at(handle).parent); break;
394+ case MTP_PROPERTY_STORAGE_ID: packet.putUInt32(db.at(handle).storage_id); break;
395+ case MTP_PROPERTY_PARENT_OBJECT: packet.putUInt32(db.at(handle).parent); break;
396 case MTP_PROPERTY_OBJECT_FORMAT: packet.putUInt16(db.at(handle).object_format); break;
397- case MTP_PROPERTY_OBJECT_SIZE: packet.putUInt32(db.at(handle).object_size); break;
398+ case MTP_PROPERTY_OBJECT_SIZE: packet.putUInt64(db.at(handle).object_size); break;
399 case MTP_PROPERTY_DISPLAY_NAME: packet.putString(db.at(handle).display_name.c_str()); break;
400 case MTP_PROPERTY_OBJECT_FILE_NAME: packet.putString(db.at(handle).display_name.c_str()); break;
401 case MTP_PROPERTY_PERSISTENT_UID: packet.putUInt128(handle); break;
402@@ -637,9 +637,9 @@
403 else
404 packet.putUInt16(1); // files can usually be played.
405 break;
406- default: return MTP_RESPONSE_GENERAL_ERROR; break;
407+ default: return MTP_RESPONSE_GENERAL_ERROR; break;
408 }
409-
410+
411 return MTP_RESPONSE_OK;
412 }
413 catch (...) {
414@@ -676,7 +676,10 @@
415 try {
416 entry = db.at(handle);
417
418- packet.getString(buffer);
419+ if (!packet.getString(buffer)) {
420+ LOG(ERROR) << "Cannot read packet";
421+ return MTP_RESPONSE_GENERAL_ERROR;
422+ }
423 newname = strdup(buffer);
424
425 oldpath /= entry.path;
426@@ -701,7 +704,10 @@
427 case MTP_PROPERTY_PARENT_OBJECT:
428 try {
429 entry = db.at(handle);
430- entry.parent = packet.getUInt32();
431+ if (!packet.getUInt32(entry.parent)) {
432+ LOG(ERROR) << "Cannot read packet";
433+ return MTP_RESPONSE_GENERAL_ERROR;
434+ }
435 }
436 catch (...) {
437 LOG(ERROR) << "Could not change parent object for handle "
438@@ -710,7 +716,7 @@
439 }
440 default: return MTP_RESPONSE_OPERATION_NOT_SUPPORTED; break;
441 }
442-
443+
444 return MTP_RESPONSE_OK;
445 }
446
447@@ -727,7 +733,7 @@
448 break;
449 default: return MTP_RESPONSE_OPERATION_NOT_SUPPORTED; break;
450 }
451-
452+
453 return MTP_RESPONSE_OK;
454 }
455
456@@ -748,9 +754,9 @@
457
458 virtual MtpResponseCode getObjectPropertyList(
459 MtpObjectHandle handle,
460- uint32_t format,
461+ uint32_t format,
462 uint32_t property,
463- int groupCode,
464+ int groupCode,
465 int depth,
466 MtpDataPacket& packet)
467 {
468@@ -841,8 +847,8 @@
469 if (property == ALL_PROPERTIES || property == MTP_PROPERTY_OBJECT_SIZE) {
470 packet.putUInt32(i);
471 packet.putUInt16(MTP_PROPERTY_OBJECT_SIZE);
472- packet.putUInt16(MTP_TYPE_UINT32);
473- packet.putUInt32(entry.object_size);
474+ packet.putUInt16(MTP_TYPE_UINT64);
475+ packet.putUInt64(entry.object_size);
476 }
477
478 // Object File Name
479@@ -944,11 +950,16 @@
480 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
481
482 try {
483+ uint64_t object_size = db.at(handle).object_size;
484+
485 info.mHandle = handle;
486 info.mStorageID = db.at(handle).storage_id;
487 info.mFormat = db.at(handle).object_format;
488 info.mProtectionStatus = 0x0;
489- info.mCompressedSize = db.at(handle).object_size;
490+ if (object_size > UINT64_C(0xFFFFFFFF))
491+ info.mCompressedSize = UINT64_C(0xFFFFFFFF);
492+ else
493+ info.mCompressedSize = object_size;
494 info.mImagePixWidth = 0;
495 info.mImagePixHeight = 0;
496 info.mImagePixDepth = 0;
497@@ -1116,7 +1127,7 @@
498 case MTP_PROPERTY_STORAGE_ID: result = new MtpProperty(property, MTP_TYPE_UINT32, false); break;
499 case MTP_PROPERTY_PARENT_OBJECT: result = new MtpProperty(property, MTP_TYPE_UINT32, true); break;
500 case MTP_PROPERTY_OBJECT_FORMAT: result = new MtpProperty(property, MTP_TYPE_UINT16, false); break;
501- case MTP_PROPERTY_OBJECT_SIZE: result = new MtpProperty(property, MTP_TYPE_UINT32, false); break;
502+ case MTP_PROPERTY_OBJECT_SIZE: result = new MtpProperty(property, MTP_TYPE_UINT64, false); break;
503 case MTP_PROPERTY_WIDTH: result = new MtpProperty(property, MTP_TYPE_UINT32, false); break;
504 case MTP_PROPERTY_HEIGHT: result = new MtpProperty(property, MTP_TYPE_UINT32, false); break;
505 case MTP_PROPERTY_IMAGE_BIT_DEPTH: result = new MtpProperty(property, MTP_TYPE_UINT32, false); break;
506@@ -1130,9 +1141,9 @@
507 case MTP_PROPERTY_DATE_MODIFIED: result = new MtpProperty(property, MTP_TYPE_STR, false); break;
508 case MTP_PROPERTY_HIDDEN: result = new MtpProperty(property, MTP_TYPE_UINT16, false); break;
509 case MTP_PROPERTY_NON_CONSUMABLE: result = new MtpProperty(property, MTP_TYPE_UINT16, false); break;
510- default: break;
511+ default: break;
512 }
513-
514+
515 return result;
516 }
517
518@@ -1146,12 +1157,12 @@
519 case MTP_DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER:
520 case MTP_DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME:
521 result = new MtpProperty(property, MTP_TYPE_STR, false); break;
522- default: break;
523+ default: break;
524 }
525-
526+
527 return result;
528 }
529-
530+
531 virtual void sessionStarted(MtpServer* server)
532 {
533 VLOG(1) << __PRETTY_FUNCTION__;
534
535=== modified file 'server/server.cpp'
536--- server/server.cpp 2015-02-13 15:19:49 +0000
537+++ server/server.cpp 2016-04-12 16:15:02 +0000
538@@ -21,6 +21,7 @@
539 #include <MtpStorage.h>
540
541 #include <iostream>
542+#include <stdint.h>
543
544 #include <signal.h>
545 #include <sys/types.h>
546@@ -39,7 +40,7 @@
547 #include <core/dbus/property.h>
548 #include <core/dbus/service.h>
549 #include <core/dbus/signal.h>
550-
551+
552 #include <core/dbus/asio/executor.h>
553 #include <core/dbus/types/stl/tuple.h>
554 #include <core/dbus/types/stl/vector.h>
555@@ -76,23 +77,23 @@
556 };
557 }
558
559-namespace core
560-{
561-namespace dbus
562-{
563+namespace core
564+{
565+namespace dbus
566+{
567 namespace traits
568-{
569-template<>
570-struct Service<core::UnityGreeter>
571-{
572+{
573+template<>
574+struct Service<core::UnityGreeter>
575+{
576 inline static const std::string& interface_name()
577 {
578 static const std::string s
579 {
580 "com.canonical.UnityGreeter"
581- };
582+ };
583 return s;
584- }
585+ }
586 };
587 }
588 }
589@@ -148,13 +149,14 @@
590 {
591 static int storageID = MTP_STORAGE_REMOVABLE_RAM;
592
593+ /* TODO check removable file system type to set maximum file size */
594 MtpStorage *removable = new MtpStorage(
595- storageID,
596+ storageID,
597 path,
598 name,
599 1024 * 1024 * 100, /* 100 MB reserved space, to avoid filling the disk */
600 true,
601- 1024 * 1024 * 1024 * 2 /* 2GB arbitrary max file size */);
602+ UINT64_C(4294967295) /* 4GB-1, we assume vfat here */);
603
604 storageID++;
605
606@@ -195,15 +197,15 @@
607 std::size_t transferred)
608 {
609 size_t processed = 0;
610-
611+
612 while(transferred - processed >= sizeof(inotify_event))
613 {
614 const char* cdata = processed + asio::buffer_cast<const char*>(buf.data());
615 const inotify_event* ievent = reinterpret_cast<const inotify_event*>(cdata);
616 path storage_path ("/media");
617-
618+
619 processed += sizeof(inotify_event) + ievent->len;
620-
621+
622 storage_path /= userdata->pw_name;
623
624 if (ievent->len > 0 && ievent->mask & IN_CREATE)
625@@ -277,11 +279,11 @@
626
627 // MTP server
628 server = new MtpServer(
629- fd,
630+ fd,
631 mtp_database,
632- false,
633- userdata->pw_gid,
634- FileSystemConfig::file_perm,
635+ false,
636+ userdata->pw_gid,
637+ FileSystemConfig::file_perm,
638 FileSystemConfig::directory_perm);
639
640 // security / screen locking
641@@ -302,12 +304,12 @@
642 property_get ("ro.product.model", product_name, "Ubuntu Touch device");
643
644 home_storage = new MtpStorage(
645- MTP_STORAGE_FIXED_RAM,
646+ MTP_STORAGE_FIXED_RAM,
647 userdata->pw_dir,
648 product_name,
649 1024 * 1024 * 100, /* 100 MB reserved space, to avoid filling the disk */
650 false,
651- 1024 * 1024 * 1024 * 2 /* 2GB arbitrary max file size */);
652+ 0 /* Do not check sizes for internal storage */);
653 mtp_database->addStoragePath(std::string(userdata->pw_dir) + "/Documents",
654 gettext("Documents"),
655 MTP_STORAGE_FIXED_RAM, false);
656@@ -424,7 +426,7 @@
657 LOG(ERROR) << "Error opening /dev/mtp_usb, aborting now...";
658 return 1;
659 }
660-
661+
662 try {
663 MtpDaemon *d = new MtpDaemon(fd);
664
665
666=== modified file 'src/MtpDataPacket.cpp'
667--- src/MtpDataPacket.cpp 2014-03-27 15:45:30 +0000
668+++ src/MtpDataPacket.cpp 2016-04-12 16:15:02 +0000
669@@ -53,104 +53,178 @@
670 MtpPacket::putUInt32(MTP_CONTAINER_TRANSACTION_ID_OFFSET, id);
671 }
672
673-uint16_t MtpDataPacket::getUInt16() {
674- int offset = mOffset;
675- uint16_t result = (uint16_t)mBuffer[offset] | ((uint16_t)mBuffer[offset + 1] << 8);
676- mOffset += 2;
677- return result;
678-}
679-
680-uint32_t MtpDataPacket::getUInt32() {
681- int offset = mOffset;
682- uint32_t result = (uint32_t)mBuffer[offset] | ((uint32_t)mBuffer[offset + 1] << 8) |
683+bool MtpDataPacket::getUInt8(uint8_t& value) {
684+ if (mPacketSize - mOffset < sizeof(value))
685+ return false;
686+ value = mBuffer[mOffset++];
687+ return true;
688+}
689+
690+bool MtpDataPacket::getUInt16(uint16_t& value) {
691+ if (mPacketSize - mOffset < sizeof(value))
692+ return false;
693+ size_t offset = mOffset;
694+ value = (uint16_t)mBuffer[offset] | ((uint16_t)mBuffer[offset + 1] << 8);
695+ mOffset += sizeof(value);
696+ return true;
697+}
698+
699+bool MtpDataPacket::getUInt32(uint32_t& value) {
700+ if (mPacketSize - mOffset < sizeof(value))
701+ return false;
702+ size_t offset = mOffset;
703+ value = (uint32_t)mBuffer[offset] | ((uint32_t)mBuffer[offset + 1] << 8) |
704 ((uint32_t)mBuffer[offset + 2] << 16) | ((uint32_t)mBuffer[offset + 3] << 24);
705- mOffset += 4;
706- return result;
707+ mOffset += sizeof(value);
708+ return true;
709 }
710
711-uint64_t MtpDataPacket::getUInt64() {
712- int offset = mOffset;
713- uint64_t result = (uint64_t)mBuffer[offset] | ((uint64_t)mBuffer[offset + 1] << 8) |
714+bool MtpDataPacket::getUInt64(uint64_t& value) {
715+ if (mPacketSize - mOffset < sizeof(value))
716+ return false;
717+ size_t offset = mOffset;
718+ value = (uint64_t)mBuffer[offset] | ((uint64_t)mBuffer[offset + 1] << 8) |
719 ((uint64_t)mBuffer[offset + 2] << 16) | ((uint64_t)mBuffer[offset + 3] << 24) |
720 ((uint64_t)mBuffer[offset + 4] << 32) | ((uint64_t)mBuffer[offset + 5] << 40) |
721 ((uint64_t)mBuffer[offset + 6] << 48) | ((uint64_t)mBuffer[offset + 7] << 56);
722- mOffset += 8;
723- return result;
724-}
725-
726-void MtpDataPacket::getUInt128(uint128_t& value) {
727- value[0] = getUInt32();
728- value[1] = getUInt32();
729- value[2] = getUInt32();
730- value[3] = getUInt32();
731-}
732-
733-void MtpDataPacket::getString(MtpStringBuffer& string)
734+ mOffset += sizeof(value);
735+ return true;
736+}
737+
738+bool MtpDataPacket::getUInt128(uint128_t& value) {
739+ return getUInt32(value[0]) && getUInt32(value[1]) && getUInt32(value[2]) && getUInt32(value[3]);
740+}
741+
742+bool MtpDataPacket::getString(MtpStringBuffer& string)
743 {
744- string.readFromPacket(this);
745+ return string.readFromPacket(this);
746 }
747
748 Int8List* MtpDataPacket::getAInt8() {
749+ uint32_t count;
750+ if (!getUInt32(count))
751+ return NULL;
752 Int8List* result = new Int8List;
753- int count = getUInt32();
754- for (int i = 0; i < count; i++)
755- result->push_back(getInt8());
756+ for (uint32_t i = 0; i < count; i++) {
757+ int8_t value;
758+ if (!getInt8(value)) {
759+ delete result;
760+ return NULL;
761+ }
762+ result->push_back(value);
763+ }
764 return result;
765 }
766
767 UInt8List* MtpDataPacket::getAUInt8() {
768+ uint32_t count;
769+ if (!getUInt32(count))
770+ return NULL;
771 UInt8List* result = new UInt8List;
772- int count = getUInt32();
773- for (int i = 0; i < count; i++)
774- result->push_back(getUInt8());
775+ for (uint32_t i = 0; i < count; i++) {
776+ uint8_t value;
777+ if (!getUInt8(value)) {
778+ delete result;
779+ return NULL;
780+ }
781+ result->push_back(value);
782+ }
783 return result;
784 }
785
786 Int16List* MtpDataPacket::getAInt16() {
787+ uint32_t count;
788+ if (!getUInt32(count))
789+ return NULL;
790 Int16List* result = new Int16List;
791- int count = getUInt32();
792- for (int i = 0; i < count; i++)
793- result->push_back(getInt16());
794+ for (uint32_t i = 0; i < count; i++) {
795+ int16_t value;
796+ if (!getInt16(value)) {
797+ delete result;
798+ return NULL;
799+ }
800+ result->push_back(value);
801+ }
802 return result;
803 }
804
805 UInt16List* MtpDataPacket::getAUInt16() {
806+ uint32_t count;
807+ if (!getUInt32(count))
808+ return NULL;
809 UInt16List* result = new UInt16List;
810- int count = getUInt32();
811- for (int i = 0; i < count; i++)
812- result->push_back(getUInt16());
813+ for (uint32_t i = 0; i < count; i++) {
814+ uint16_t value;
815+ if (!getUInt16(value)) {
816+ delete result;
817+ return NULL;
818+ }
819+ result->push_back(value);
820+ }
821 return result;
822 }
823
824 Int32List* MtpDataPacket::getAInt32() {
825+ uint32_t count;
826+ if (!getUInt32(count))
827+ return NULL;
828 Int32List* result = new Int32List;
829- int count = getUInt32();
830- for (int i = 0; i < count; i++)
831- result->push_back(getInt32());
832+ for (uint32_t i = 0; i < count; i++) {
833+ int32_t value;
834+ if (!getInt32(value)) {
835+ delete result;
836+ return NULL;
837+ }
838+ result->push_back(value);
839+ }
840 return result;
841 }
842
843 UInt32List* MtpDataPacket::getAUInt32() {
844+ uint32_t count;
845+ if (!getUInt32(count))
846+ return NULL;
847 UInt32List* result = new UInt32List;
848- int count = getUInt32();
849- for (int i = 0; i < count; i++)
850- result->push_back(getUInt32());
851+ for (uint32_t i = 0; i < count; i++) {
852+ uint32_t value;
853+ if (!getUInt32(value)) {
854+ delete result;
855+ return NULL;
856+ }
857+ result->push_back(value);
858+ }
859 return result;
860 }
861
862 Int64List* MtpDataPacket::getAInt64() {
863+ uint32_t count;
864+ if (!getUInt32(count))
865+ return NULL;
866 Int64List* result = new Int64List;
867- int count = getUInt32();
868- for (int i = 0; i < count; i++)
869- result->push_back(getInt64());
870+ for (uint32_t i = 0; i < count; i++) {
871+ int64_t value;
872+ if (!getInt64(value)) {
873+ delete result;
874+ return NULL;
875+ }
876+ result->push_back(value);
877+ }
878 return result;
879 }
880
881 UInt64List* MtpDataPacket::getAUInt64() {
882+ uint32_t count;
883+ if (!getUInt32(count))
884+ return NULL;
885 UInt64List* result = new UInt64List;
886- int count = getUInt32();
887- for (int i = 0; i < count; i++)
888- result->push_back(getUInt64());
889+ for (uint32_t i = 0; i < count; i++) {
890+ uint64_t value;
891+ if (!getUInt64(value)) {
892+ delete result;
893+ return NULL;
894+ }
895+ result->push_back(value);
896+ }
897 return result;
898 }
899
900@@ -333,7 +407,7 @@
901
902 void MtpDataPacket::putString(const uint16_t* string) {
903 int count = 0;
904- for (int i = 0; i < 256; i++) {
905+ for (int i = 0; i <= MTP_STRING_MAX_CHARACTER_NUMBER; i++) {
906 if (string[i])
907 count++;
908 else
909@@ -365,7 +439,7 @@
910 }
911
912 int MtpDataPacket::writeData(int fd, void* data, uint32_t length) {
913- allocate(length);
914+ allocate(length + MTP_CONTAINER_HEADER_SIZE);
915 memcpy(mBuffer + MTP_CONTAINER_HEADER_SIZE, data, length);
916 length += MTP_CONTAINER_HEADER_SIZE;
917 MtpPacket::putUInt32(MTP_CONTAINER_LENGTH_OFFSET, length);
918@@ -420,7 +494,7 @@
919 // Queue a read request. Call readDataWait to wait for result
920 int MtpDataPacket::readDataAsync(struct usb_request *req) {
921 if (usb_request_queue(req)) {
922- PLOG(ERROR) << "usb_endpoint_queue failed";
923+ PLOG(ERROR) << "usb_endpoint_queue failed, errno: " << errno;
924 return -1;
925 }
926 return 0;
927
928=== modified file 'src/MtpDevice.cpp'
929--- src/MtpDevice.cpp 2014-03-27 15:45:30 +0000
930+++ src/MtpDevice.cpp 2016-04-12 16:15:02 +0000
931@@ -135,13 +135,22 @@
932 struct usb_endpoint_descriptor *ep_in_desc = NULL;
933 struct usb_endpoint_descriptor *ep_out_desc = NULL;
934 struct usb_endpoint_descriptor *ep_intr_desc = NULL;
935+ //USB3 add USB_DT_SS_ENDPOINT_COMP as companion descriptor;
936+ struct usb_ss_ep_comp_descriptor *ep_ss_ep_comp_desc = NULL;
937 for (int i = 0; i < 3; i++) {
938 ep = (struct usb_endpoint_descriptor *)usb_descriptor_iter_next(&iter);
939+ if (ep && ep->bDescriptorType == USB_DT_SS_ENDPOINT_COMP) {
940+ VLOG(2) << "Descriptor type is USB_DT_SS_ENDPOINT_COMP for USB3 \n";
941+ ep_ss_ep_comp_desc = (usb_ss_ep_comp_descriptor*)ep;
942+ ep = (struct usb_endpoint_descriptor *)usb_descriptor_iter_next(&iter);
943+ }
944+
945 if (!ep || ep->bDescriptorType != USB_DT_ENDPOINT) {
946 LOG(ERROR) << "endpoints not found";
947 usb_device_close(device);
948 return NULL;
949 }
950+
951 if (ep->bmAttributes == USB_ENDPOINT_XFER_BULK) {
952 if (ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
953 ep_in_desc = ep;
954@@ -199,7 +208,7 @@
955
956 MtpDevice::~MtpDevice() {
957 close();
958- for (int i = 0; i < mDeviceProperties.size(); i++)
959+ for (size_t i = 0; i < mDeviceProperties.size(); i++)
960 delete mDeviceProperties[i];
961 usb_request_free(mRequestIn1);
962 usb_request_free(mRequestIn2);
963@@ -257,7 +266,7 @@
964 VLOG(2) << "*** FORMAT: " << MtpDebug::getFormatCodeName(format);
965 MtpObjectPropertyList* props = getObjectPropsSupported(format);
966 if (props) {
967- for (int j = 0; j < props->size(); j++) {
968+ for (size_t j = 0; j < props->size(); j++) {
969 MtpObjectProperty prop = (*props)[j];
970 MtpProperty* property = getObjectPropDesc(prop, format);
971 if (property) {
972@@ -317,8 +326,10 @@
973 MtpResponseCode ret = readResponse();
974 if (ret == MTP_RESPONSE_OK) {
975 MtpDeviceInfo* info = new MtpDeviceInfo;
976- info->read(mData);
977- return info;
978+ if (info->read(mData))
979+ return info;
980+ else
981+ delete info;
982 }
983 return NULL;
984 }
985@@ -350,8 +361,10 @@
986 MtpResponseCode ret = readResponse();
987 if (ret == MTP_RESPONSE_OK) {
988 MtpStorageInfo* info = new MtpStorageInfo(storageID);
989- info->read(mData);
990- return info;
991+ if (info->read(mData))
992+ return info;
993+ else
994+ delete info;
995 }
996 return NULL;
997 }
998@@ -389,8 +402,10 @@
999 MtpResponseCode ret = readResponse();
1000 if (ret == MTP_RESPONSE_OK) {
1001 MtpObjectInfo* info = new MtpObjectInfo(handle);
1002- info->read(mData);
1003- return info;
1004+ if (info->read(mData))
1005+ return info;
1006+ else
1007+ delete info;
1008 }
1009 return NULL;
1010 }
1011@@ -551,8 +566,10 @@
1012 MtpResponseCode ret = readResponse();
1013 if (ret == MTP_RESPONSE_OK) {
1014 MtpProperty* property = new MtpProperty;
1015- property->read(mData);
1016- return property;
1017+ if (property->read(mData))
1018+ return property;
1019+ else
1020+ delete property;
1021 }
1022 return NULL;
1023 }
1024@@ -570,15 +587,17 @@
1025 MtpResponseCode ret = readResponse();
1026 if (ret == MTP_RESPONSE_OK) {
1027 MtpProperty* property = new MtpProperty;
1028- property->read(mData);
1029- return property;
1030+ if (property->read(mData))
1031+ return property;
1032+ else
1033+ delete property;
1034 }
1035 return NULL;
1036 }
1037
1038 bool MtpDevice::readObject(MtpObjectHandle handle,
1039 bool (* callback)(void* data, int offset, int length, void* clientData),
1040- int objectSize, void* clientData) {
1041+ size_t objectSize, void* clientData) {
1042 MtpAutolock autoLock(mMutex);
1043 bool result = false;
1044
1045@@ -671,7 +690,7 @@
1046 // reads the object's data and writes it to the specified file path
1047 bool MtpDevice::readObject(MtpObjectHandle handle, const char* destPath, int group, int perm) {
1048 VLOG(2) << "readObject: " << destPath;
1049- int fd = ::open(destPath, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
1050+ int fd = ::open(destPath, O_RDWR | O_CREAT | O_TRUNC | O_LARGEFILE, S_IRUSR | S_IWUSR);
1051 if (fd < 0) {
1052 LOG(ERROR) << "open failed for " << destPath;
1053 return false;
1054
1055=== modified file 'src/MtpDeviceInfo.cpp'
1056--- src/MtpDeviceInfo.cpp 2014-03-27 15:45:30 +0000
1057+++ src/MtpDeviceInfo.cpp 2016-04-12 16:15:02 +0000
1058@@ -32,7 +32,7 @@
1059 mVendorExtensionID(0),
1060 mVendorExtensionVersion(0),
1061 mVendorExtensionDesc(NULL),
1062- mFunctionalCode(0),
1063+ mFunctionalMode(0),
1064 mOperations(NULL),
1065 mEvents(NULL),
1066 mDeviceProperties(NULL),
1067@@ -63,32 +63,39 @@
1068 free(mSerial);
1069 }
1070
1071-void MtpDeviceInfo::read(MtpDataPacket& packet) {
1072+bool MtpDeviceInfo::read(MtpDataPacket& packet) {
1073 MtpStringBuffer string;
1074
1075 // read the device info
1076- mStandardVersion = packet.getUInt16();
1077- mVendorExtensionID = packet.getUInt32();
1078- mVendorExtensionVersion = packet.getUInt16();
1079+ if (!packet.getUInt16(mStandardVersion)) return false;
1080+ if (!packet.getUInt32(mVendorExtensionID)) return false;
1081+ if (!packet.getUInt16(mVendorExtensionVersion)) return false;
1082
1083- packet.getString(string);
1084+ if (!packet.getString(string)) return false;
1085 mVendorExtensionDesc = strdup((const char *)string);
1086
1087- mFunctionalCode = packet.getUInt16();
1088+ if (!packet.getUInt16(mFunctionalMode)) return false;
1089 mOperations = packet.getAUInt16();
1090+ if (!mOperations) return false;
1091 mEvents = packet.getAUInt16();
1092+ if (!mEvents) return false;
1093 mDeviceProperties = packet.getAUInt16();
1094+ if (!mDeviceProperties) return false;
1095 mCaptureFormats = packet.getAUInt16();
1096+ if (!mCaptureFormats) return false;
1097 mPlaybackFormats = packet.getAUInt16();
1098+ if (!mCaptureFormats) return false;
1099
1100- packet.getString(string);
1101+ if (!packet.getString(string)) return false;
1102 mManufacturer = strdup((const char *)string);
1103- packet.getString(string);
1104+ if (!packet.getString(string)) return false;
1105 mModel = strdup((const char *)string);
1106- packet.getString(string);
1107+ if (!packet.getString(string)) return false;
1108 mVersion = strdup((const char *)string);
1109- packet.getString(string);
1110+ if (!packet.getString(string)) return false;
1111 mSerial = strdup((const char *)string);
1112+
1113+ return true;
1114 }
1115
1116 void MtpDeviceInfo::print() {
1117@@ -97,7 +104,7 @@
1118 << "\n\tmVendorExtensionID: " << mVendorExtensionID
1119 << "\n\tmVendorExtensionVersion: " << mVendorExtensionVersion
1120 << "\n\tmVendorExtensionDesc: " << mVendorExtensionDesc
1121- << "\n\tmFunctionalCode: " << mFunctionalCode
1122+ << "\n\tmFunctionalCode: " << mFunctionalMode
1123 << "\n\tmManufacturer: " << mManufacturer
1124 << "\n\tmModel: " << mModel
1125 << "\n\tmVersion: " << mVersion
1126
1127=== modified file 'src/MtpObjectInfo.cpp'
1128--- src/MtpObjectInfo.cpp 2014-03-27 15:45:30 +0000
1129+++ src/MtpObjectInfo.cpp 2016-04-12 16:15:02 +0000
1130@@ -60,39 +60,41 @@
1131 free(mKeywords);
1132 }
1133
1134-void MtpObjectInfo::read(MtpDataPacket& packet) {
1135+bool MtpObjectInfo::read(MtpDataPacket& packet) {
1136 MtpStringBuffer string;
1137 time_t time;
1138
1139- mStorageID = packet.getUInt32();
1140- mFormat = packet.getUInt16();
1141- mProtectionStatus = packet.getUInt16();
1142- mCompressedSize = packet.getUInt32();
1143- mThumbFormat = packet.getUInt16();
1144- mThumbCompressedSize = packet.getUInt32();
1145- mThumbPixWidth = packet.getUInt32();
1146- mThumbPixHeight = packet.getUInt32();
1147- mImagePixWidth = packet.getUInt32();
1148- mImagePixHeight = packet.getUInt32();
1149- mImagePixDepth = packet.getUInt32();
1150- mParent = packet.getUInt32();
1151- mAssociationType = packet.getUInt16();
1152- mAssociationDesc = packet.getUInt32();
1153- mSequenceNumber = packet.getUInt32();
1154+ if (!packet.getUInt32(mStorageID)) return false;
1155+ if (!packet.getUInt16(mFormat)) return false;
1156+ if (!packet.getUInt16(mProtectionStatus)) return false;
1157+ if (!packet.getUInt32(mCompressedSize)) return false;
1158+ if (!packet.getUInt16(mThumbFormat)) return false;
1159+ if (!packet.getUInt32(mThumbCompressedSize)) return false;
1160+ if (!packet.getUInt32(mThumbPixWidth)) return false;
1161+ if (!packet.getUInt32(mThumbPixHeight)) return false;
1162+ if (!packet.getUInt32(mImagePixWidth)) return false;
1163+ if (!packet.getUInt32(mImagePixHeight)) return false;
1164+ if (!packet.getUInt32(mImagePixDepth)) return false;
1165+ if (!packet.getUInt32(mParent)) return false;
1166+ if (!packet.getUInt16(mAssociationType)) return false;
1167+ if (!packet.getUInt32(mAssociationDesc)) return false;
1168+ if (!packet.getUInt32(mSequenceNumber)) return false;
1169
1170- packet.getString(string);
1171+ if (!packet.getString(string)) return false;
1172 mName = strdup((const char *)string);
1173
1174- packet.getString(string);
1175+ if (!packet.getString(string)) return false;
1176 if (parseDateTime((const char*)string, time))
1177 mDateCreated = time;
1178
1179- packet.getString(string);
1180+ if (!packet.getString(string)) return false;
1181 if (parseDateTime((const char*)string, time))
1182 mDateModified = time;
1183
1184- packet.getString(string);
1185+ if (!packet.getString(string)) return false;
1186 mKeywords = strdup((const char *)string);
1187+
1188+ return true;
1189 }
1190
1191 void MtpObjectInfo::print() {
1192
1193=== modified file 'src/MtpPacket.cpp'
1194--- src/MtpPacket.cpp 2014-03-27 15:45:30 +0000
1195+++ src/MtpPacket.cpp 2016-04-12 16:15:02 +0000
1196@@ -52,7 +52,7 @@
1197 memset(mBuffer, 0, mBufferSize);
1198 }
1199
1200-void MtpPacket::allocate(int length) {
1201+void MtpPacket::allocate(size_t length) {
1202 if (length > mBufferSize) {
1203 int newLength = length + mAllocationIncrement;
1204 mBuffer = (uint8_t *)realloc(mBuffer, newLength);
1205
1206=== modified file 'src/MtpProperty.cpp'
1207--- src/MtpProperty.cpp 2014-03-27 15:45:30 +0000
1208+++ src/MtpProperty.cpp 2016-04-12 16:15:02 +0000
1209@@ -112,15 +112,15 @@
1210 free(mMinimumValue.str);
1211 free(mMaximumValue.str);
1212 if (mDefaultArrayValues) {
1213- for (int i = 0; i < mDefaultArrayLength; i++)
1214+ for (uint32_t i = 0; i < mDefaultArrayLength; i++)
1215 free(mDefaultArrayValues[i].str);
1216 }
1217 if (mCurrentArrayValues) {
1218- for (int i = 0; i < mCurrentArrayLength; i++)
1219+ for (uint32_t i = 0; i < mCurrentArrayLength; i++)
1220 free(mCurrentArrayValues[i].str);
1221 }
1222 if (mEnumValues) {
1223- for (int i = 0; i < mEnumLength; i++)
1224+ for (uint16_t i = 0; i < mEnumLength; i++)
1225 free(mEnumValues[i].str);
1226 }
1227 }
1228@@ -129,11 +129,14 @@
1229 delete[] mEnumValues;
1230 }
1231
1232-void MtpProperty::read(MtpDataPacket& packet) {
1233- mCode = packet.getUInt16();
1234+bool MtpProperty::read(MtpDataPacket& packet) {
1235+ uint8_t temp8;
1236+
1237+ if (!packet.getUInt16(mCode)) return false;
1238 bool deviceProp = isDeviceProperty();
1239- mType = packet.getUInt16();
1240- mWriteable = (packet.getUInt8() == 1);
1241+ if (!packet.getUInt16(mType)) return false;
1242+ if (!packet.getUInt8(temp8)) return false;
1243+ mWriteable = (temp8 == 1);
1244 switch (mType) {
1245 case MTP_TYPE_AINT8:
1246 case MTP_TYPE_AUINT8:
1247@@ -146,28 +149,36 @@
1248 case MTP_TYPE_AINT128:
1249 case MTP_TYPE_AUINT128:
1250 mDefaultArrayValues = readArrayValues(packet, mDefaultArrayLength);
1251- if (deviceProp)
1252+ if (!mDefaultArrayValues) return false;
1253+ if (deviceProp) {
1254 mCurrentArrayValues = readArrayValues(packet, mCurrentArrayLength);
1255+ if (!mCurrentArrayValues) return false;
1256+ }
1257 break;
1258 default:
1259- readValue(packet, mDefaultValue);
1260- if (deviceProp)
1261- readValue(packet, mCurrentValue);
1262- }
1263- if (!deviceProp)
1264- mGroupCode = packet.getUInt32();
1265- mFormFlag = packet.getUInt8();
1266+ if (!readValue(packet, mDefaultValue)) return false;
1267+ if (deviceProp) {
1268+ if (!readValue(packet, mCurrentValue)) return false;
1269+ }
1270+ }
1271+ if (!deviceProp) {
1272+ if (!packet.getUInt32(mGroupCode)) return false;
1273+ }
1274+ if (!packet.getUInt8(mFormFlag)) return false;
1275
1276 if (mFormFlag == kFormRange) {
1277- readValue(packet, mMinimumValue);
1278- readValue(packet, mMaximumValue);
1279- readValue(packet, mStepSize);
1280+ if (!readValue(packet, mMinimumValue)) return false;
1281+ if (!readValue(packet, mMaximumValue)) return false;
1282+ if (!readValue(packet, mStepSize)) return false;
1283 } else if (mFormFlag == kFormEnum) {
1284- mEnumLength = packet.getUInt16();
1285+ if (!packet.getUInt16(mEnumLength)) return false;
1286 mEnumValues = new MtpPropertyValue[mEnumLength];
1287- for (int i = 0; i < mEnumLength; i++)
1288- readValue(packet, mEnumValues[i]);
1289+ for (int i = 0; i < mEnumLength; i++) {
1290+ if (!readValue(packet, mEnumValues[i])) return false;
1291+ }
1292 }
1293+
1294+ return true;
1295 }
1296
1297 void MtpProperty::write(MtpDataPacket& packet) {
1298@@ -197,9 +208,9 @@
1299 if (deviceProp)
1300 writeValue(packet, mCurrentValue);
1301 }
1302- packet.putUInt32(mGroupCode);
1303 if (!deviceProp)
1304- packet.putUInt8(mFormFlag);
1305+ packet.putUInt32(mGroupCode);
1306+ packet.putUInt8(mFormFlag);
1307 if (mFormFlag == kFormRange) {
1308 writeValue(packet, mMinimumValue);
1309 writeValue(packet, mMaximumValue);
1310@@ -423,52 +434,52 @@
1311 buffer += ss.str();
1312 }
1313
1314-void MtpProperty::readValue(MtpDataPacket& packet, MtpPropertyValue& value) {
1315+bool MtpProperty::readValue(MtpDataPacket& packet, MtpPropertyValue& value) {
1316 MtpStringBuffer stringBuffer;
1317
1318 switch (mType) {
1319 case MTP_TYPE_INT8:
1320 case MTP_TYPE_AINT8:
1321- value.u.i8 = packet.getInt8();
1322+ if (!packet.getInt8(value.u.i8)) return false;
1323 break;
1324 case MTP_TYPE_UINT8:
1325 case MTP_TYPE_AUINT8:
1326- value.u.u8 = packet.getUInt8();
1327+ if (!packet.getUInt8(value.u.u8)) return false;
1328 break;
1329 case MTP_TYPE_INT16:
1330 case MTP_TYPE_AINT16:
1331- value.u.i16 = packet.getInt16();
1332+ if (!packet.getInt16(value.u.i16)) return false;
1333 break;
1334 case MTP_TYPE_UINT16:
1335 case MTP_TYPE_AUINT16:
1336- value.u.u16 = packet.getUInt16();
1337+ if (!packet.getUInt16(value.u.u16)) return false;
1338 break;
1339 case MTP_TYPE_INT32:
1340 case MTP_TYPE_AINT32:
1341- value.u.i32 = packet.getInt32();
1342+ if (!packet.getInt32(value.u.i32)) return false;
1343 break;
1344 case MTP_TYPE_UINT32:
1345 case MTP_TYPE_AUINT32:
1346- value.u.u32 = packet.getUInt32();
1347+ if (!packet.getUInt32(value.u.u32)) return false;
1348 break;
1349 case MTP_TYPE_INT64:
1350 case MTP_TYPE_AINT64:
1351- value.u.i64 = packet.getInt64();
1352+ if (!packet.getInt64(value.u.i64)) return false;
1353 break;
1354 case MTP_TYPE_UINT64:
1355 case MTP_TYPE_AUINT64:
1356- value.u.u64 = packet.getUInt64();
1357+ if (!packet.getUInt64(value.u.u64)) return false;
1358 break;
1359 case MTP_TYPE_INT128:
1360 case MTP_TYPE_AINT128:
1361- packet.getInt128(value.u.i128);
1362+ if (!packet.getInt128(value.u.i128)) return false;
1363 break;
1364 case MTP_TYPE_UINT128:
1365 case MTP_TYPE_AUINT128:
1366- packet.getUInt128(value.u.u128);
1367+ if (!packet.getUInt128(value.u.u128)) return false;
1368 break;
1369 case MTP_TYPE_STR:
1370- packet.getString(stringBuffer);
1371+ if (!packet.getString(stringBuffer)) return false;
1372 value.str = strdup(stringBuffer);
1373 break;
1374 default:
1375@@ -476,6 +487,7 @@
1376 << std::hex << mType << std::dec
1377 << " in MtpProperty::readValue";
1378 }
1379+ return true;
1380 }
1381
1382 void MtpProperty::writeValue(MtpDataPacket& packet, MtpPropertyValue& value) {
1383@@ -535,19 +547,28 @@
1384 }
1385 }
1386
1387-MtpPropertyValue* MtpProperty::readArrayValues(MtpDataPacket& packet, int& length) {
1388- length = packet.getUInt32();
1389- if (length == 0)
1390+MtpPropertyValue* MtpProperty::readArrayValues(MtpDataPacket& packet, uint32_t& length) {
1391+ if (!packet.getUInt32(length)) return NULL;
1392+
1393+ // Fail if resulting array is over 2GB. This is because the maximum array
1394+ // size may be less than SIZE_MAX on some platforms.
1395+ if (length == 0 ||
1396+ length >= INT32_MAX / sizeof(MtpPropertyValue)) {
1397+ length = 0;
1398 return NULL;
1399+ }
1400 MtpPropertyValue* result = new MtpPropertyValue[length];
1401- for (int i = 0; i < length; i++)
1402- readValue(packet, result[i]);
1403+ for (uint32_t i = 0; i < length; i++)
1404+ if (!readValue(packet, result[i])) {
1405+ delete result;
1406+ return NULL;
1407+ }
1408 return result;
1409 }
1410
1411-void MtpProperty::writeArrayValues(MtpDataPacket& packet, MtpPropertyValue* values, int length) {
1412+void MtpProperty::writeArrayValues(MtpDataPacket& packet, MtpPropertyValue* values, uint32_t length) {
1413 packet.putUInt32(length);
1414- for (int i = 0; i < length; i++)
1415+ for (uint32_t i = 0; i < length; i++)
1416 writeValue(packet, values[i]);
1417 }
1418
1419
1420=== modified file 'src/MtpRequestPacket.cpp'
1421--- src/MtpRequestPacket.cpp 2013-06-13 10:22:21 +0000
1422+++ src/MtpRequestPacket.cpp 2016-04-12 16:15:02 +0000
1423@@ -24,11 +24,13 @@
1424 #include "MtpRequestPacket.h"
1425
1426 #include <usbhost/usbhost.h>
1427+#include <glog/logging.h>
1428
1429 namespace android {
1430
1431 MtpRequestPacket::MtpRequestPacket()
1432- : MtpPacket(512)
1433+ : MtpPacket(512),
1434+ mParameterCount(0)
1435 {
1436 }
1437
1438@@ -38,10 +40,21 @@
1439 #ifdef MTP_DEVICE
1440 int MtpRequestPacket::read(int fd) {
1441 int ret = ::read(fd, mBuffer, mBufferSize);
1442- if (ret >= 0)
1443+ if (ret < 0) {
1444+ // file read error
1445+ return ret;
1446+ }
1447+
1448+ // request packet should have 12 byte header followed by 0 to 5 32-bit arguments
1449+ if (ret >= MTP_CONTAINER_HEADER_SIZE
1450+ && ret <= MTP_CONTAINER_HEADER_SIZE + 5 * sizeof(uint32_t)
1451+ && ((ret - MTP_CONTAINER_HEADER_SIZE) & 3) == 0) {
1452 mPacketSize = ret;
1453- else
1454- mPacketSize = 0;
1455+ mParameterCount = (ret - MTP_CONTAINER_HEADER_SIZE) / sizeof(uint32_t);
1456+ } else {
1457+ PLOG(ERROR) << "Malformed MTP request packet";
1458+ ret = -1;
1459+ }
1460 return ret;
1461 }
1462 #endif
1463
1464=== modified file 'src/MtpServer.cpp'
1465--- src/MtpServer.cpp 2014-10-31 15:56:55 +0000
1466+++ src/MtpServer.cpp 2016-04-12 16:15:02 +0000
1467@@ -27,6 +27,7 @@
1468 #include <sys/stat.h>
1469 #include <dirent.h>
1470 #include <unistd.h>
1471+#include <errno.h>
1472
1473 #define LOG_TAG "MtpServer"
1474
1475@@ -99,6 +100,7 @@
1476 MTP_EVENT_OBJECT_REMOVED,
1477 MTP_EVENT_STORE_ADDED,
1478 MTP_EVENT_STORE_REMOVED,
1479+ MTP_EVENT_DEVICE_PROP_CHANGED,
1480 MTP_EVENT_OBJECT_INFO_CHANGED,
1481 MTP_EVENT_OBJECT_PROP_CHANGED,
1482 };
1483@@ -132,7 +134,7 @@
1484 void MtpServer::removeStorage(MtpStorage* storage) {
1485 MtpAutolock autoLock(mMutex);
1486
1487- for (int i = 0; i < mStorages.size(); i++) {
1488+ for (size_t i = 0; i < mStorages.size(); i++) {
1489 if (mStorages[i] == storage) {
1490 mStorages.erase(mStorages.begin()+i);
1491 sendStoreRemoved(storage->getStorageID());
1492@@ -144,7 +146,7 @@
1493 MtpStorage* MtpServer::getStorage(MtpStorageID id) {
1494 if (id == 0)
1495 return mStorages[0];
1496- for (int i = 0; i < mStorages.size(); i++) {
1497+ for (size_t i = 0; i < mStorages.size(); i++) {
1498 MtpStorage* storage = mStorages[i];
1499 if (storage->getStorageID() == id)
1500 return storage;
1501@@ -226,10 +228,12 @@
1502 VLOG(2) << "sending response "
1503 << std::hex << mResponse.getResponseCode() << std::dec;
1504 ret = mResponse.write(fd);
1505+ const int savedErrno = errno;
1506 mResponse.dump();
1507 if (ret < 0) {
1508- PLOG(ERROR) << "request write returned " << ret;
1509- if (errno == ECANCELED) {
1510+ PLOG(ERROR) << "request write returned " << ret
1511+ << ", errno: " << savedErrno;
1512+ if (savedErrno == ECANCELED) {
1513 // return to top of loop and wait for next command
1514 continue;
1515 }
1516@@ -348,6 +352,15 @@
1517 mSendObjectHandle = kInvalidObjectHandle;
1518 }
1519
1520+ int containertype = mRequest.getContainerType();
1521+ if (containertype != MTP_CONTAINER_TYPE_COMMAND) {
1522+ LOG(ERROR) << "wrong container type " << containertype;
1523+ return false;
1524+ }
1525+
1526+ VLOG(2) << "got command " << MtpDebug::getOperationCodeName(operation)
1527+ << ", " << std::hex << operation << std::dec;
1528+
1529 switch (operation) {
1530 case MTP_OPERATION_GET_DEVICE_INFO:
1531 response = doGetDeviceInfo();
1532@@ -513,6 +526,9 @@
1533 mResponse.setParameter(1, mSessionID);
1534 return MTP_RESPONSE_SESSION_ALREADY_OPEN;
1535 }
1536+ if (mRequest.getParameterCount() < 1)
1537+ return MTP_RESPONSE_INVALID_PARAMETER;
1538+
1539 mSessionID = mRequest.getParameter(1);
1540 mSessionOpen = true;
1541
1542@@ -547,6 +563,9 @@
1543
1544 if (!mSessionOpen)
1545 return MTP_RESPONSE_SESSION_NOT_OPEN;
1546+ if (mRequest.getParameterCount() < 1)
1547+ return MTP_RESPONSE_INVALID_PARAMETER;
1548+
1549 MtpStorageID id = mRequest.getParameter(1);
1550 MtpStorage* storage = getStorage(id);
1551 if (!storage)
1552@@ -568,6 +587,8 @@
1553 MtpResponseCode MtpServer::doGetObjectPropsSupported() {
1554 if (!mSessionOpen)
1555 return MTP_RESPONSE_SESSION_NOT_OPEN;
1556+ if (mRequest.getParameterCount() < 1)
1557+ return MTP_RESPONSE_INVALID_PARAMETER;
1558 MtpObjectFormat format = mRequest.getParameter(1);
1559 MtpObjectPropertyList* properties = mDatabase->getSupportedObjectProperties(format);
1560 mData.putAUInt16(properties);
1561@@ -578,6 +599,8 @@
1562 MtpResponseCode MtpServer::doGetObjectHandles() {
1563 if (!mSessionOpen)
1564 return MTP_RESPONSE_SESSION_NOT_OPEN;
1565+ if (mRequest.getParameterCount() < 3)
1566+ return MTP_RESPONSE_INVALID_PARAMETER;
1567 MtpStorageID storageID = mRequest.getParameter(1); // 0xFFFFFFFF for all storage
1568 MtpObjectFormat format = mRequest.getParameter(2); // 0 for all formats
1569 MtpObjectHandle parent = mRequest.getParameter(3); // 0xFFFFFFFF for objects with no parent
1570@@ -595,6 +618,8 @@
1571 MtpResponseCode MtpServer::doGetNumObjects() {
1572 if (!mSessionOpen)
1573 return MTP_RESPONSE_SESSION_NOT_OPEN;
1574+ if (mRequest.getParameterCount() < 3)
1575+ return MTP_RESPONSE_INVALID_PARAMETER;
1576 MtpStorageID storageID = mRequest.getParameter(1); // 0xFFFFFFFF for all storage
1577 MtpObjectFormat format = mRequest.getParameter(2); // 0 for all formats
1578 MtpObjectHandle parent = mRequest.getParameter(3); // 0xFFFFFFFF for objects with no parent
1579@@ -617,6 +642,8 @@
1580 return MTP_RESPONSE_SESSION_NOT_OPEN;
1581 if (!hasStorage())
1582 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
1583+ if (mRequest.getParameterCount() < 1)
1584+ return MTP_RESPONSE_INVALID_PARAMETER;
1585 MtpObjectHandle handle = mRequest.getParameter(1);
1586
1587 // FIXME - check for invalid object handle
1588@@ -635,9 +662,13 @@
1589 return MTP_RESPONSE_SESSION_NOT_OPEN;
1590 if (!hasStorage())
1591 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
1592+ if (mRequest.getParameterCount() < 1)
1593+ return MTP_RESPONSE_INVALID_PARAMETER;
1594 MtpStorageID handle = mRequest.getParameter(1);
1595
1596 MtpObjectHandleList* references = mData.getAUInt32();
1597+ if (!references)
1598+ return MTP_RESPONSE_INVALID_PARAMETER;
1599 MtpResponseCode result = mDatabase->setObjectReferences(handle, references);
1600 delete references;
1601 return result;
1602@@ -646,6 +677,8 @@
1603 MtpResponseCode MtpServer::doGetObjectPropValue() {
1604 if (!hasStorage())
1605 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
1606+ if (mRequest.getParameterCount() < 2)
1607+ return MTP_RESPONSE_INVALID_PARAMETER;
1608 MtpObjectHandle handle = mRequest.getParameter(1);
1609 MtpObjectProperty property = mRequest.getParameter(2);
1610 VLOG(2) << "GetObjectPropValue " << handle
1611@@ -659,6 +692,8 @@
1612
1613 if (!hasStorage())
1614 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
1615+ if (mRequest.getParameterCount() < 2)
1616+ return MTP_RESPONSE_INVALID_PARAMETER;
1617 MtpObjectHandle handle = mRequest.getParameter(1);
1618 MtpObjectProperty property = mRequest.getParameter(2);
1619 VLOG(2) << "SetObjectPropValue " << handle
1620@@ -672,6 +707,8 @@
1621 }
1622
1623 MtpResponseCode MtpServer::doGetDevicePropValue() {
1624+ if (mRequest.getParameterCount() < 1)
1625+ return MTP_RESPONSE_INVALID_PARAMETER;
1626 MtpDeviceProperty property = mRequest.getParameter(1);
1627 VLOG(1) << "GetDevicePropValue " << MtpDebug::getDevicePropCodeName(property);
1628
1629@@ -679,6 +716,8 @@
1630 }
1631
1632 MtpResponseCode MtpServer::doSetDevicePropValue() {
1633+ if (mRequest.getParameterCount() < 1)
1634+ return MTP_RESPONSE_INVALID_PARAMETER;
1635 MtpDeviceProperty property = mRequest.getParameter(1);
1636 VLOG(1) << "SetDevicePropValue " << MtpDebug::getDevicePropCodeName(property);
1637
1638@@ -686,6 +725,8 @@
1639 }
1640
1641 MtpResponseCode MtpServer::doResetDevicePropValue() {
1642+ if (mRequest.getParameterCount() < 1)
1643+ return MTP_RESPONSE_INVALID_PARAMETER;
1644 MtpDeviceProperty property = mRequest.getParameter(1);
1645 VLOG(1) << "ResetDevicePropValue " << MtpDebug::getDevicePropCodeName(property);
1646
1647@@ -695,6 +736,8 @@
1648 MtpResponseCode MtpServer::doGetObjectPropList() {
1649 if (!hasStorage())
1650 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
1651+ if (mRequest.getParameterCount() < 5)
1652+ return MTP_RESPONSE_INVALID_PARAMETER;
1653
1654 MtpObjectHandle handle = mRequest.getParameter(1);
1655 // use uint32_t so we can support 0xFFFFFFFF
1656@@ -714,6 +757,8 @@
1657 MtpResponseCode MtpServer::doGetObjectInfo() {
1658 if (!hasStorage())
1659 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
1660+ if (mRequest.getParameterCount() < 1)
1661+ return MTP_RESPONSE_INVALID_PARAMETER;
1662 MtpObjectHandle handle = mRequest.getParameter(1);
1663 MtpObjectInfo info(handle);
1664 MtpResponseCode result = mDatabase->getObjectInfo(handle, info);
1665@@ -754,6 +799,8 @@
1666 MtpResponseCode MtpServer::doGetObject() {
1667 if (!hasStorage())
1668 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
1669+ if (mRequest.getParameterCount() < 1)
1670+ return MTP_RESPONSE_INVALID_PARAMETER;
1671 MtpObjectHandle handle = mRequest.getParameter(1);
1672 MtpString pathBuf;
1673 int64_t fileLength;
1674@@ -763,7 +810,7 @@
1675 return result;
1676
1677 mtp_file_range mfr;
1678- mfr.fd = open(pathBuf.c_str(), O_RDONLY);
1679+ mfr.fd = open(pathBuf.c_str(), O_RDONLY | O_LARGEFILE);
1680 if (mfr.fd < 0) {
1681 return MTP_RESPONSE_GENERAL_ERROR;
1682 }
1683@@ -774,18 +821,24 @@
1684
1685 // then transfer the file
1686 int ret = ioctl(mFD, MTP_SEND_FILE_WITH_HEADER, (unsigned long)&mfr);
1687+ if (ret < 0) {
1688+ if (errno == ECANCELED) {
1689+ result = MTP_RESPONSE_TRANSACTION_CANCELLED;
1690+ } else {
1691+ result = MTP_RESPONSE_GENERAL_ERROR;
1692+ }
1693+ } else {
1694+ result = MTP_RESPONSE_OK;
1695+ }
1696+
1697 VLOG(2) << "MTP_SEND_FILE_WITH_HEADER returned " << ret;
1698 close(mfr.fd);
1699- if (ret < 0) {
1700- if (errno == ECANCELED)
1701- return MTP_RESPONSE_TRANSACTION_CANCELLED;
1702- else
1703- return MTP_RESPONSE_GENERAL_ERROR;
1704- }
1705- return MTP_RESPONSE_OK;
1706+ return result;
1707 }
1708
1709 MtpResponseCode MtpServer::doGetThumb() {
1710+ if (mRequest.getParameterCount() < 1)
1711+ return MTP_RESPONSE_INVALID_PARAMETER;
1712 MtpObjectHandle handle = mRequest.getParameter(1);
1713 size_t thumbSize;
1714 void* thumb = mDatabase->getThumbnail(handle, thumbSize);
1715@@ -809,11 +862,19 @@
1716 uint32_t length;
1717 offset = mRequest.getParameter(2);
1718 if (operation == MTP_OPERATION_GET_PARTIAL_OBJECT_64) {
1719+ // MTP_OPERATION_GET_PARTIAL_OBJECT_64 takes 4 arguments
1720+ if (mRequest.getParameterCount() < 4)
1721+ return MTP_RESPONSE_INVALID_PARAMETER;
1722+
1723 // android extension with 64 bit offset
1724 uint64_t offset2 = mRequest.getParameter(3);
1725 offset = offset | (offset2 << 32);
1726 length = mRequest.getParameter(4);
1727 } else {
1728+ // MTP_OPERATION_GET_PARTIAL_OBJECT takes 3 arguments
1729+ if (mRequest.getParameterCount() < 3)
1730+ return MTP_RESPONSE_INVALID_PARAMETER;
1731+
1732 // standard GetPartialObject
1733 length = mRequest.getParameter(3);
1734 }
1735@@ -823,11 +884,11 @@
1736 int result = mDatabase->getObjectFilePath(handle, pathBuf, fileLength, format);
1737 if (result != MTP_RESPONSE_OK)
1738 return result;
1739- if (offset + length > fileLength)
1740+ if (offset + length > (uint64_t)fileLength)
1741 length = fileLength - offset;
1742
1743 mtp_file_range mfr;
1744- mfr.fd = open(pathBuf.c_str(), O_RDONLY);
1745+ mfr.fd = open(pathBuf.c_str(), O_RDONLY | O_LARGEFILE);
1746 if (mfr.fd < 0) {
1747 return MTP_RESPONSE_GENERAL_ERROR;
1748 }
1749@@ -840,18 +901,24 @@
1750 // transfer the file
1751 int ret = ioctl(mFD, MTP_SEND_FILE_WITH_HEADER, (unsigned long)&mfr);
1752 VLOG(2) << "MTP_SEND_FILE_WITH_HEADER returned " << ret;
1753- close(mfr.fd);
1754+ result = MTP_RESPONSE_OK;
1755 if (ret < 0) {
1756 if (errno == ECANCELED)
1757- return MTP_RESPONSE_TRANSACTION_CANCELLED;
1758+ result = MTP_RESPONSE_TRANSACTION_CANCELLED;
1759 else
1760- return MTP_RESPONSE_GENERAL_ERROR;
1761+ result = MTP_RESPONSE_GENERAL_ERROR;
1762 }
1763- return MTP_RESPONSE_OK;
1764+ close(mfr.fd);
1765+ return result;
1766 }
1767
1768 MtpResponseCode MtpServer::doSendObjectInfo() {
1769 MtpString path;
1770+ uint16_t temp16;
1771+ uint32_t temp32;
1772+
1773+ if (mRequest.getParameterCount() < 2)
1774+ return MTP_RESPONSE_INVALID_PARAMETER;
1775 MtpStorageID storageID = mRequest.getParameter(1);
1776 MtpStorage* storage = getStorage(storageID);
1777 MtpObjectHandle parent = mRequest.getParameter(2);
1778@@ -873,25 +940,29 @@
1779 }
1780
1781 // read only the fields we need
1782- mData.getUInt32(); // storage ID
1783- MtpObjectFormat format = mData.getUInt16();
1784- mData.getUInt16(); // protection status
1785- mSendObjectFileSize = mData.getUInt32();
1786- mData.getUInt16(); // thumb format
1787- mData.getUInt32(); // thumb compressed size
1788- mData.getUInt32(); // thumb pix width
1789- mData.getUInt32(); // thumb pix height
1790- mData.getUInt32(); // image pix width
1791- mData.getUInt32(); // image pix height
1792- mData.getUInt32(); // image bit depth
1793- mData.getUInt32(); // parent
1794- uint16_t associationType = mData.getUInt16();
1795- uint32_t associationDesc = mData.getUInt32(); // association desc
1796- mData.getUInt32(); // sequence number
1797+ if (!mData.getUInt32(temp32)) return MTP_RESPONSE_INVALID_PARAMETER; // storage ID
1798+ if (!mData.getUInt16(temp16)) return MTP_RESPONSE_INVALID_PARAMETER;
1799+ MtpObjectFormat format = temp16;
1800+ if (!mData.getUInt16(temp16)) return MTP_RESPONSE_INVALID_PARAMETER; // protection status
1801+ if (!mData.getUInt32(temp32)) return MTP_RESPONSE_INVALID_PARAMETER;
1802+ mSendObjectFileSize = temp32;
1803+ if (!mData.getUInt16(temp16)) return MTP_RESPONSE_INVALID_PARAMETER; // thumb format
1804+ if (!mData.getUInt32(temp32)) return MTP_RESPONSE_INVALID_PARAMETER; // thumb compressed size
1805+ if (!mData.getUInt32(temp32)) return MTP_RESPONSE_INVALID_PARAMETER; // thumb pix width
1806+ if (!mData.getUInt32(temp32)) return MTP_RESPONSE_INVALID_PARAMETER; // thumb pix height
1807+ if (!mData.getUInt32(temp32)) return MTP_RESPONSE_INVALID_PARAMETER; // image pix width
1808+ if (!mData.getUInt32(temp32)) return MTP_RESPONSE_INVALID_PARAMETER; // image pix height
1809+ if (!mData.getUInt32(temp32)) return MTP_RESPONSE_INVALID_PARAMETER; // image bit depth
1810+ if (!mData.getUInt32(temp32)) return MTP_RESPONSE_INVALID_PARAMETER; // parent
1811+ if (!mData.getUInt16(temp16)) return MTP_RESPONSE_INVALID_PARAMETER;
1812+ uint16_t associationType = temp16;
1813+ if (!mData.getUInt32(temp32)) return MTP_RESPONSE_INVALID_PARAMETER;
1814+ uint32_t associationDesc = temp32; // association desc
1815+ if (!mData.getUInt32(temp32)) return MTP_RESPONSE_INVALID_PARAMETER; // sequence number
1816 MtpStringBuffer name, created, modified;
1817- mData.getString(name); // file name
1818- mData.getString(created); // date created
1819- mData.getString(modified); // date modified
1820+ if (!mData.getString(name)) return MTP_RESPONSE_INVALID_PARAMETER; // file name
1821+ if (!mData.getString(created)) return MTP_RESPONSE_INVALID_PARAMETER; // date created
1822+ if (!mData.getString(modified)) return MTP_RESPONSE_INVALID_PARAMETER; // date modified
1823 // keywords follow
1824
1825 VLOG(2) << "name: " << (const char *) name
1826@@ -954,6 +1025,7 @@
1827 MtpResponseCode result = MTP_RESPONSE_OK;
1828 mode_t mask;
1829 int ret, initialData;
1830+ bool isCanceled = false;
1831
1832 if (mSendObjectHandle == kInvalidObjectHandle) {
1833 LOG(ERROR) << "Expected SendObjectInfo before SendObject";
1834@@ -970,7 +1042,9 @@
1835 initialData = ret - MTP_CONTAINER_HEADER_SIZE;
1836
1837 mtp_file_range mfr;
1838- mfr.fd = open(mSendObjectFilePath.c_str(), O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
1839+ mfr.fd = open(mSendObjectFilePath.c_str(),
1840+ O_RDWR | O_CREAT | O_TRUNC | O_LARGEFILE,
1841+ S_IRUSR | S_IWUSR);
1842 if (mfr.fd < 0) {
1843 result = MTP_RESPONSE_GENERAL_ERROR;
1844 goto done;
1845@@ -981,28 +1055,38 @@
1846 fchmod(mfr.fd, mFilePermission);
1847 umask(mask);
1848
1849- if (initialData > 0)
1850+ if (initialData > 0) {
1851 ret = write(mfr.fd, mData.getData(), initialData);
1852-
1853- if (mSendObjectFileSize - initialData > 0) {
1854- mfr.offset = initialData;
1855- if (mSendObjectFileSize == 0xFFFFFFFF) {
1856- // tell driver to read until it receives a short packet
1857- mfr.length = 0xFFFFFFFF;
1858- } else {
1859- mfr.length = mSendObjectFileSize - initialData;
1860+ }
1861+
1862+ if (ret < 0) {
1863+ LOG(ERROR) << "failed to write initial data";
1864+ result = MTP_RESPONSE_GENERAL_ERROR;
1865+ } else {
1866+ if (mSendObjectFileSize - initialData > 0) {
1867+ mfr.offset = initialData;
1868+ if (mSendObjectFileSize == 0xFFFFFFFF) {
1869+ // tell driver to read until it receives a short packet
1870+ mfr.length = 0xFFFFFFFF;
1871+ } else {
1872+ mfr.length = mSendObjectFileSize - initialData;
1873+ }
1874+
1875+ VLOG(2) << "receiving " << mSendObjectFilePath.c_str();
1876+ // transfer the file
1877+ ret = ioctl(mFD, MTP_RECEIVE_FILE, (unsigned long)&mfr);
1878+ if ((ret < 0) && (errno == ECANCELED)) {
1879+ isCanceled = true;
1880+ }
1881+
1882+ VLOG(2) << "MTP_RECEIVE_FILE returned " << ret;
1883 }
1884-
1885- VLOG(2) << "receiving " << mSendObjectFilePath.c_str();
1886- // transfer the file
1887- ret = ioctl(mFD, MTP_RECEIVE_FILE, (unsigned long)&mfr);
1888- VLOG(2) << "MTP_RECEIVE_FILE returned " << ret;
1889 }
1890 close(mfr.fd);
1891
1892 if (ret < 0) {
1893 unlink(mSendObjectFilePath.c_str());
1894- if (errno == ECANCELED)
1895+ if (isCanceled)
1896 result = MTP_RESPONSE_TRANSACTION_CANCELLED;
1897 else
1898 result = MTP_RESPONSE_GENERAL_ERROR;
1899@@ -1021,7 +1105,7 @@
1900
1901 static void deleteRecursive(const char* path) {
1902 char pathbuf[PATH_MAX];
1903- int pathLength = strlen(path);
1904+ size_t pathLength = strlen(path);
1905 if (pathLength >= sizeof(pathbuf) - 1) {
1906 LOG(ERROR) << "path too long: " << path;
1907 }
1908@@ -1082,8 +1166,10 @@
1909 MtpResponseCode MtpServer::doDeleteObject() {
1910 if (!hasStorage())
1911 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
1912+ if (mRequest.getParameterCount() < 1)
1913+ return MTP_RESPONSE_INVALID_PARAMETER;
1914 MtpObjectHandle handle = mRequest.getParameter(1);
1915- MtpObjectFormat format = mRequest.getParameter(2);
1916+ MtpObjectFormat format;
1917 // FIXME - support deleting all objects if handle is 0xFFFFFFFF
1918 // FIXME - implement deleting objects by format
1919
1920@@ -1127,6 +1213,8 @@
1921 }
1922
1923 MtpResponseCode MtpServer::doGetObjectPropDesc() {
1924+ if (mRequest.getParameterCount() < 2)
1925+ return MTP_RESPONSE_INVALID_PARAMETER;
1926 MtpObjectProperty propCode = mRequest.getParameter(1);
1927 MtpObjectFormat format = mRequest.getParameter(2);
1928 VLOG(2) << "GetObjectPropDesc " << MtpDebug::getObjectPropCodeName(propCode)
1929@@ -1140,6 +1228,8 @@
1930 }
1931
1932 MtpResponseCode MtpServer::doGetDevicePropDesc() {
1933+ if (mRequest.getParameterCount() < 1)
1934+ return MTP_RESPONSE_INVALID_PARAMETER;
1935 MtpDeviceProperty propCode = mRequest.getParameter(1);
1936 VLOG(1) << "GetDevicePropDesc " << MtpDebug::getDevicePropCodeName(propCode);
1937 MtpProperty* property = mDatabase->getDevicePropertyDesc(propCode);
1938@@ -1153,6 +1243,8 @@
1939 MtpResponseCode MtpServer::doSendPartialObject() {
1940 if (!hasStorage())
1941 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
1942+ if (mRequest.getParameterCount() < 4)
1943+ return MTP_RESPONSE_INVALID_PARAMETER;
1944 MtpObjectHandle handle = mRequest.getParameter(1);
1945 uint64_t offset = mRequest.getParameter(2);
1946 uint64_t offset2 = mRequest.getParameter(3);
1947@@ -1188,19 +1280,27 @@
1948 length -= initialData;
1949 }
1950
1951- if (length > 0) {
1952- mtp_file_range mfr;
1953- mfr.fd = edit->mFD;
1954- mfr.offset = offset;
1955- mfr.length = length;
1956+ bool isCanceled = false;
1957+ if (ret < 0) {
1958+ PLOG(ERROR) << "failed to write initial data";
1959+ } else {
1960+ if (length > 0) {
1961+ mtp_file_range mfr;
1962+ mfr.fd = edit->mFD;
1963+ mfr.offset = offset;
1964+ mfr.length = length;
1965
1966- // transfer the file
1967- ret = ioctl(mFD, MTP_RECEIVE_FILE, (unsigned long)&mfr);
1968- VLOG(2) << "MTP_RECEIVE_FILE returned " << ret;
1969+ // transfer the file
1970+ ret = ioctl(mFD, MTP_RECEIVE_FILE, (unsigned long)&mfr);
1971+ if ((ret < 0) && (errno == ECANCELED)) {
1972+ isCanceled = true;
1973+ }
1974+ VLOG(2) << "MTP_RECEIVE_FILE returned " << ret << " errno " << errno;
1975+ }
1976 }
1977 if (ret < 0) {
1978 mResponse.setParameter(1, 0);
1979- if (errno == ECANCELED)
1980+ if (isCanceled)
1981 return MTP_RESPONSE_TRANSACTION_CANCELLED;
1982 else
1983 return MTP_RESPONSE_GENERAL_ERROR;
1984@@ -1217,6 +1317,8 @@
1985 }
1986
1987 MtpResponseCode MtpServer::doTruncateObject() {
1988+ if (mRequest.getParameterCount() < 3)
1989+ return MTP_RESPONSE_INVALID_PARAMETER;
1990 MtpObjectHandle handle = mRequest.getParameter(1);
1991 ObjectEdit* edit = getEditObject(handle);
1992 if (!edit) {
1993@@ -1236,6 +1338,8 @@
1994 }
1995
1996 MtpResponseCode MtpServer::doBeginEditObject() {
1997+ if (mRequest.getParameterCount() < 1)
1998+ return MTP_RESPONSE_INVALID_PARAMETER;
1999 MtpObjectHandle handle = mRequest.getParameter(1);
2000 if (getEditObject(handle)) {
2001 LOG(ERROR) << "object already open for edit in doBeginEditObject";
2002@@ -1249,7 +1353,7 @@
2003 if (result != MTP_RESPONSE_OK)
2004 return result;
2005
2006- int fd = open(path.c_str(), O_RDWR | O_EXCL);
2007+ int fd = open(path.c_str(), O_RDWR | O_EXCL | O_LARGEFILE);
2008 if (fd < 0) {
2009 PLOG(ERROR) << "open failed for " << path.c_str() << " in doBeginEditObject";
2010 return MTP_RESPONSE_GENERAL_ERROR;
2011@@ -1260,6 +1364,8 @@
2012 }
2013
2014 MtpResponseCode MtpServer::doEndEditObject() {
2015+ if (mRequest.getParameterCount() < 1)
2016+ return MTP_RESPONSE_INVALID_PARAMETER;
2017 MtpObjectHandle handle = mRequest.getParameter(1);
2018 ObjectEdit* edit = getEditObject(handle);
2019 if (!edit) {
2020
2021=== modified file 'src/MtpStorageInfo.cpp'
2022--- src/MtpStorageInfo.cpp 2014-03-27 15:45:30 +0000
2023+++ src/MtpStorageInfo.cpp 2016-04-12 16:15:02 +0000
2024@@ -48,21 +48,23 @@
2025 free(mVolumeIdentifier);
2026 }
2027
2028-void MtpStorageInfo::read(MtpDataPacket& packet) {
2029+bool MtpStorageInfo::read(MtpDataPacket& packet) {
2030 MtpStringBuffer string;
2031
2032 // read the device info
2033- mStorageType = packet.getUInt16();
2034- mFileSystemType = packet.getUInt16();
2035- mAccessCapability = packet.getUInt16();
2036- mMaxCapacity = packet.getUInt64();
2037- mFreeSpaceBytes = packet.getUInt64();
2038- mFreeSpaceObjects = packet.getUInt32();
2039+ if (!packet.getUInt16(mStorageType)) return false;
2040+ if (!packet.getUInt16(mFileSystemType)) return false;
2041+ if (!packet.getUInt16(mAccessCapability)) return false;
2042+ if (!packet.getUInt64(mMaxCapacity)) return false;
2043+ if (!packet.getUInt64(mFreeSpaceBytes)) return false;
2044+ if (!packet.getUInt32(mFreeSpaceObjects)) return false;
2045
2046- packet.getString(string);
2047+ if (!packet.getString(string)) return false;
2048 mStorageDescription = strdup((const char *)string);
2049- packet.getString(string);
2050+ if (!packet.getString(string)) return false;
2051 mVolumeIdentifier = strdup((const char *)string);
2052+
2053+ return true;
2054 }
2055
2056 void MtpStorageInfo::print() {
2057
2058=== modified file 'src/MtpStringBuffer.cpp'
2059--- src/MtpStringBuffer.cpp 2013-06-13 10:22:21 +0000
2060+++ src/MtpStringBuffer.cpp 2016-04-12 16:15:02 +0000
2061@@ -56,42 +56,47 @@
2062 }
2063
2064 void MtpStringBuffer::set(const char* src) {
2065- int length = strlen(src);
2066- if (length >= sizeof(mBuffer))
2067- length = sizeof(mBuffer) - 1;
2068- memcpy(mBuffer, src, length);
2069-
2070 // count the characters
2071 int count = 0;
2072 char ch;
2073- while ((ch = *src++) != 0) {
2074+ char* dest = (char*)mBuffer;
2075+
2076+ while ((ch = *src++) != 0 && count < MTP_STRING_MAX_CHARACTER_NUMBER) {
2077 if ((ch & 0x80) == 0) {
2078 // single byte character
2079+ *dest++ = ch;
2080 } else if ((ch & 0xE0) == 0xC0) {
2081 // two byte character
2082- if (! *src++) {
2083+ char ch1 = *src++;
2084+ if (! ch1) {
2085 // last character was truncated, so ignore last byte
2086- length--;
2087 break;
2088 }
2089+
2090+ *dest++ = ch;
2091+ *dest++ = ch1;
2092 } else if ((ch & 0xF0) == 0xE0) {
2093 // 3 byte char
2094- if (! *src++) {
2095- // last character was truncated, so ignore last byte
2096- length--;
2097- break;
2098- }
2099- if (! *src++) {
2100- // last character was truncated, so ignore last two bytes
2101- length -= 2;
2102- break;
2103- }
2104+ char ch1 = *src++;
2105+ if (! ch1) {
2106+ // last character was truncated, so ignore last byte
2107+ break;
2108+ }
2109+ char ch2 = *src++;
2110+ if (! ch2) {
2111+ // last character was truncated, so ignore last byte
2112+ break;
2113+ }
2114+
2115+ *dest++ = ch;
2116+ *dest++ = ch1;
2117+ *dest++ = ch2;
2118 }
2119 count++;
2120 }
2121
2122- mByteCount = length + 1;
2123- mBuffer[length] = 0;
2124+ *dest++ = 0;
2125+ mByteCount = dest - (char*)mBuffer;
2126 mCharCount = count;
2127 }
2128
2129@@ -100,7 +105,7 @@
2130 uint16_t ch;
2131 uint8_t* dest = mBuffer;
2132
2133- while ((ch = *src++) != 0 && count < 255) {
2134+ while ((ch = *src++) != 0 && count < MTP_STRING_MAX_CHARACTER_NUMBER) {
2135 if (ch >= 0x0800) {
2136 *dest++ = (uint8_t)(0xE0 | (ch >> 12));
2137 *dest++ = (uint8_t)(0x80 | ((ch >> 6) & 0x3F));
2138@@ -118,11 +123,17 @@
2139 mByteCount = dest - mBuffer;
2140 }
2141
2142-void MtpStringBuffer::readFromPacket(MtpDataPacket* packet) {
2143- int count = packet->getUInt8();
2144+bool MtpStringBuffer::readFromPacket(MtpDataPacket* packet) {
2145+ uint8_t count;
2146+ if (!packet->getUInt8(count))
2147+ return false;
2148+
2149 uint8_t* dest = mBuffer;
2150 for (int i = 0; i < count; i++) {
2151- uint16_t ch = packet->getUInt16();
2152+ uint16_t ch;
2153+
2154+ if (!packet->getUInt16(ch))
2155+ return false;
2156 if (ch >= 0x0800) {
2157 *dest++ = (uint8_t)(0xE0 | (ch >> 12));
2158 *dest++ = (uint8_t)(0x80 | ((ch >> 6) & 0x3F));
2159@@ -137,6 +148,7 @@
2160 *dest++ = 0;
2161 mCharCount = count;
2162 mByteCount = dest - mBuffer;
2163+ return true;
2164 }
2165
2166 void MtpStringBuffer::writeToPacket(MtpDataPacket* packet) const {
2167
2168=== modified file 'src/MtpUtils.cpp'
2169--- src/MtpUtils.cpp 2013-09-10 15:54:14 +0000
2170+++ src/MtpUtils.cpp 2016-04-12 16:15:02 +0000
2171@@ -19,7 +19,6 @@
2172 #include <stdio.h>
2173 #include <time.h>
2174
2175-// #include <cutils/tztime.h>
2176 #include "MtpUtils.h"
2177
2178 namespace android {
2179@@ -31,40 +30,40 @@
2180 DD replaced by the day (01-31), T is a constant character 'T' delimiting time from date,
2181 hh is replaced by the hour (00-23), mm is replaced by the minute (00-59), and ss by the
2182 second (00-59). The ".s" is optional, and represents tenths of a second.
2183+This is followed by a UTC offset given as "[+-]zzzz" or the literal "Z", meaning UTC.
2184 */
2185
2186 bool parseDateTime(const char* dateTime, time_t& outSeconds) {
2187 int year, month, day, hour, minute, second;
2188- struct tm tm;
2189-
2190 if (sscanf(dateTime, "%04d%02d%02dT%02d%02d%02d",
2191- &year, &month, &day, &hour, &minute, &second) != 6)
2192+ &year, &month, &day, &hour, &minute, &second) != 6)
2193 return false;
2194+
2195+ // skip optional tenth of second
2196 const char* tail = dateTime + 15;
2197- // skip optional tenth of second
2198- if (tail[0] == '.' && tail[1])
2199- tail += 2;
2200- //FIXME - support +/-hhmm
2201+ if (tail[0] == '.' && tail[1]) tail += 2;
2202+
2203+ // FIXME: "Z" means UTC, but non-"Z" doesn't mean local time.
2204+ // It might be that you're in Asia/Seoul on vacation and your Android
2205+ // device has noticed this via the network, but your camera was set to
2206+ // America/Los_Angeles once when you bought it and doesn't know where
2207+ // it is right now, so the camera says "20160106T081700-0800" but we
2208+ // just ignore the "-0800" and assume local time which is actually "+0900".
2209+ // I think to support this (without switching to Java or using icu4c)
2210+ // you'd want to always use timegm(3) and then manually add/subtract
2211+ // the UTC offset parsed from the string (taking care of wrapping).
2212+ // mktime(3) ignores the tm_gmtoff field, so you can't let it do the work.
2213 bool useUTC = (tail[0] == 'Z');
2214
2215- // hack to compute timezone
2216- time_t dummy;
2217- tzset();
2218- localtime_r(&dummy, &tm);
2219-
2220+ struct tm tm = {};
2221 tm.tm_sec = second;
2222 tm.tm_min = minute;
2223 tm.tm_hour = hour;
2224 tm.tm_mday = day;
2225 tm.tm_mon = month - 1; // mktime uses months in 0 - 11 range
2226 tm.tm_year = year - 1900;
2227- tm.tm_wday = 0;
2228 tm.tm_isdst = -1;
2229- outSeconds = mktime(&tm);
2230- /*if (useUTC)
2231- outSeconds = mktime(&tm);
2232- else
2233- outSeconds = mktime_tz(&tm, tm.tm_zone);*/
2234+ outSeconds = useUTC ? timegm(&tm) : mktime(&tm);
2235
2236 return true;
2237 }
2238@@ -74,7 +73,7 @@
2239
2240 localtime_r(&seconds, &tm);
2241 snprintf(buffer, bufferLength, "%04d%02d%02dT%02d%02d%02d",
2242- tm.tm_year + 1900,
2243+ tm.tm_year + 1900,
2244 tm.tm_mon + 1, // localtime_r uses months in 0 - 11 range
2245 tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
2246 }
2247
2248=== modified file 'tests/TestMtpUtils.cpp'
2249--- tests/TestMtpUtils.cpp 2013-09-13 14:21:51 +0000
2250+++ tests/TestMtpUtils.cpp 2016-04-12 16:15:02 +0000
2251@@ -40,7 +40,7 @@
2252 {
2253 time_t seconds = 1378726903;
2254 char buffer[25];
2255- char *expected = "20130909T114143";
2256+ const char *expected = "20130909T114143";
2257
2258 setenv("TZ", "UTC", 1);
2259

Subscribers

People subscribed via source and target branches

to all changes: