1 /*
2 * Copyright (c) 2024-2025 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "mtpfs_mtp_device.h"
17
18 #include <sstream>
19 #include <thread>
20 #include <unistd.h>
21
22 #include "mtpfs_fuse.h"
23 #include "mtpfs_libmtp.h"
24 #include "mtpfs_util.h"
25 #include "storage_service_log.h"
26
27 const int32_t FETCH_NUM = 3000;
28 const int32_t DIR_COUNT_ONE = 1;
29 bool g_eventFlag = true;
30 uint32_t MtpFsDevice::rootNode_ = ~0;
31
MtpFsDevice()32 MtpFsDevice::MtpFsDevice() : device_(nullptr), capabilities_(), deviceMutex_(), rootDir_(), moveEnabled_(false)
33 {
34 MtpFsUtil::Off();
35 LIBMTP_Init();
36 MtpFsUtil::On();
37 }
38
~MtpFsDevice()39 MtpFsDevice::~MtpFsDevice()
40 {
41 LOGI("MtpFsDevice Destructor.");
42 eventFlag_ = false;
43 Disconnect();
44 }
45
Connect(LIBMTP_raw_device_t * dev)46 bool MtpFsDevice::Connect(LIBMTP_raw_device_t *dev)
47 {
48 if (device_) {
49 LOGI("Already connected");
50 return true;
51 }
52
53 // Do not output LIBMTP debug stuff
54 MtpFsUtil::Off();
55 device_ = LIBMTP_Open_Raw_Device_Uncached(dev);
56 MtpFsUtil::On();
57
58 if (!device_) {
59 LIBMTP_Dump_Errorstack(device_);
60 return false;
61 }
62
63 if (!EnumStorages())
64 return false;
65
66 // Retrieve capabilities.
67 capabilities_ = MtpFsDevice::GetCapabilities(*this);
68
69 LOGI("Connected");
70 return true;
71 }
72
ConvertErrorCode(LIBMTP_error_number_t err)73 bool MtpFsDevice::ConvertErrorCode(LIBMTP_error_number_t err)
74 {
75 if (err != LIBMTP_ERROR_NONE) {
76 switch (err) {
77 case LIBMTP_ERROR_NO_DEVICE_ATTACHED:
78 LOGE("No raw devices found");
79 break;
80 case LIBMTP_ERROR_CONNECTING:
81 LOGE("There has been an error connecting. Exiting");
82 break;
83 case LIBMTP_ERROR_MEMORY_ALLOCATION:
84 LOGE("Encountered a Memory Allocation Error. Exiting");
85 break;
86 case LIBMTP_ERROR_GENERAL:
87 LOGE("General error occurred. Exiting");
88 break;
89 case LIBMTP_ERROR_USB_LAYER:
90 LOGE("USB Layer error occurred. Exiting");
91 break;
92 default:
93 break;
94 }
95 return false;
96 }
97 return true;
98 }
99
HandleDevNum(const std::string & devFile,int & devNo,int rawDevicesCnt,LIBMTP_raw_device_t * rawDevices)100 void MtpFsDevice::HandleDevNum(const std::string &devFile, int &devNo, int rawDevicesCnt,
101 LIBMTP_raw_device_t *rawDevices)
102 {
103 uint8_t bnum;
104 uint8_t dnum;
105 if (SmtpfsUsbDevPath(devFile, &bnum, &dnum)) {
106 for (devNo = 0; devNo < rawDevicesCnt; ++devNo) {
107 if (bnum == rawDevices[devNo].bus_location && dnum == rawDevices[devNo].devnum) {
108 break;
109 }
110 }
111 }
112 }
113
Libmtp_event_cb_fn(int ret,LIBMTP_event_t event,uint32_t param,void * data)114 static void Libmtp_event_cb_fn(int ret, LIBMTP_event_t event, uint32_t param, void *data)
115 {
116 (void)data;
117 LOGI("LIBMTP_event_cb_fn, ret=%{public}d, event=%{public}d, param=%{public}d.", ret, event, param);
118 g_eventFlag = true;
119 }
120
ConnectByDevNo(int devNo)121 bool MtpFsDevice::ConnectByDevNo(int devNo)
122 {
123 LOGI("Start to connect by device number = %{public}d.", devNo);
124 if (device_) {
125 LOGI("Already connected");
126 return true;
127 }
128 int rawDevicesCnt;
129 LIBMTP_raw_device_t *rawDevices;
130 MtpFsUtil::Off();
131 LIBMTP_error_number_t err = LIBMTP_Detect_Raw_Devices(&rawDevices, &rawDevicesCnt);
132 MtpFsUtil::On();
133 if (!ConvertErrorCode(err)) {
134 return false;
135 }
136
137 if (devNo < 0 || devNo >= rawDevicesCnt) {
138 LOGE("Can not connect to device no %{public}d", devNo + 1);
139 free(static_cast<void *>(rawDevices));
140 return false;
141 }
142 LIBMTP_raw_device_t *rawDevice = &rawDevices[devNo];
143
144 MtpFsUtil::Off();
145 device_ = LIBMTP_Open_Raw_Device_Uncached(rawDevice);
146 MtpFsUtil::On();
147 free(static_cast<void *>(rawDevices));
148 if (!device_) {
149 LOGE("device_ is nullptr");
150 LIBMTP_Dump_Errorstack(device_);
151 return false;
152 }
153 if (!EnumStorages()) {
154 LOGE("EnumStorages failed.");
155 return false;
156 }
157 capabilities_ = MtpFsDevice::GetCapabilities(*this);
158 std::thread([this]() { ReadEvent(); }).detach();
159 LOGI("Connect by device number success.");
160 return true;
161 }
162
ReadEvent()163 void MtpFsDevice::ReadEvent()
164 {
165 while (eventFlag_) {
166 if (g_eventFlag) {
167 int ret = LIBMTP_Read_Event_Async(device_, Libmtp_event_cb_fn, nullptr);
168 g_eventFlag = false;
169 }
170 }
171 LOGI("Device detached, read event end");
172 }
173
ConnectByDevFile(const std::string & devFile)174 bool MtpFsDevice::ConnectByDevFile(const std::string &devFile)
175 {
176 if (device_) {
177 LOGE("Already connected");
178 return true;
179 }
180
181 LIBMTP_raw_device_t *rawDevice = SmtpfsRawDeviceNew(devFile);
182 if (!rawDevice) {
183 LOGE("Can not open such device: %{public}s", devFile.c_str());
184 return false;
185 }
186
187 bool rval = Connect(rawDevice);
188 SmtpfsRawDeviceFree(rawDevice);
189 return rval;
190 }
191
Disconnect()192 void MtpFsDevice::Disconnect()
193 {
194 if (!device_) {
195 return;
196 }
197 LIBMTP_Release_Device(device_);
198 device_ = nullptr;
199 LOGI("Disconnected");
200 }
201
StorageTotalSize() const202 uint64_t MtpFsDevice::StorageTotalSize() const
203 {
204 uint64_t total = 0;
205 for (LIBMTP_devicestorage_t *s = device_->storage; s; s = s->next) {
206 total += s->MaxCapacity;
207 }
208 return total;
209 }
210
StorageFreeSize() const211 uint64_t MtpFsDevice::StorageFreeSize() const
212 {
213 uint64_t free = 0;
214 for (LIBMTP_devicestorage_t *s = device_->storage; s; s = s->next) {
215 free += s->FreeSpaceInBytes;
216 }
217 return free;
218 }
219
EnumStorages()220 bool MtpFsDevice::EnumStorages()
221 {
222 LOGI("Start to enum mtp device storages.");
223 CriticalEnter();
224 LIBMTP_Clear_Errorstack(device_);
225 if (LIBMTP_Get_Storage(device_, LIBMTP_STORAGE_SORTBY_NOTSORTED) < 0) {
226 LOGE("Could not retrieve device storage.");
227 LIBMTP_Dump_Errorstack(device_);
228 LIBMTP_Clear_Errorstack(device_);
229 return false;
230 }
231 CriticalLeave();
232 LOGI("Enum mtp device storages success.");
233 return true;
234 }
235
HandleDir(LIBMTP_file_t * content,MtpFsTypeDir * dir)236 const void MtpFsDevice::HandleDir(LIBMTP_file_t *content, MtpFsTypeDir *dir)
237 {
238 if (content == nullptr || dir == nullptr) {
239 LOGE("content or dir is nullptr");
240 return;
241 }
242 for (LIBMTP_file_t *f = content; f; f = f->next) {
243 if (f->filetype == LIBMTP_FILETYPE_FOLDER) {
244 dir->AddDir(MtpFsTypeDir(f));
245 } else {
246 dir->AddFile(MtpFsTypeFile(f));
247 }
248 }
249 }
250
HandleDirByFetch(LIBMTP_file_t * content,MtpFsTypeDir * dir)251 const void MtpFsDevice::HandleDirByFetch(LIBMTP_file_t *content, MtpFsTypeDir *dir)
252 {
253 if (content == nullptr) {
254 LOGE("directory have not any content");
255 dir->dirs_.clear();
256 dir->files_.clear();
257 return;
258 }
259 if (dir == nullptr) {
260 LOGE("dir is nullptr");
261 return;
262 }
263 LOGI("HandleDir clear dir content");
264 dir->dirs_.clear();
265 dir->files_.clear();
266 for (LIBMTP_file_t *f = content; f; f = f->next) {
267 if (f->filetype == LIBMTP_FILETYPE_FOLDER) {
268 dir->AddDir(MtpFsTypeDir(f));
269 } else {
270 dir->AddFile(MtpFsTypeFile(f));
271 }
272 }
273 }
274
DirFetchContent(std::string path)275 const MtpFsTypeDir *MtpFsDevice::DirFetchContent(std::string path)
276 {
277 if (!rootDir_.IsFetched()) {
278 for (LIBMTP_devicestorage_t *s = device_->storage; s; s = s->next) {
279 rootDir_.AddDir(MtpFsTypeDir(rootNode_, 0, s->id, std::string(s->StorageDescription)));
280 rootDir_.SetFetched();
281 }
282 }
283
284 if (rootDir_.DirCount() == 1) {
285 path = '/' + rootDir_.Dirs().begin()->Name() + path;
286 }
287 if (path == "/") {
288 return &rootDir_;
289 }
290
291 std::string member;
292 std::istringstream ss(path);
293 MtpFsTypeDir *dir = &rootDir_;
294 while (std::getline(ss, member, '/')) {
295 if (member.empty()) {
296 continue;
297 }
298 const MtpFsTypeDir *tmp = dir->Dir(member);
299 if (!tmp && !dir->IsFetched()) {
300 CriticalEnter();
301 LIBMTP_file_t *content = LIBMTP_Get_Files_And_Folders(device_, dir->StorageId(), dir->Id());
302 CriticalLeave();
303 HandleDir(content, dir);
304 LIBMTPFreeFilesAndFolders(&content);
305 dir->SetFetched();
306 tmp = dir->Dir(member);
307 }
308 if (!tmp) {
309 return nullptr;
310 }
311 dir = const_cast<MtpFsTypeDir *>(tmp);
312 }
313
314 if (dir->IsFetched()) {
315 return dir;
316 }
317 CriticalEnter();
318 dir->SetFetched();
319 LIBMTP_file_t *content = LIBMTP_Get_Files_And_Folders(device_, dir->StorageId(), dir->Id());
320 CriticalLeave();
321 HandleDir(content, dir);
322 LIBMTPFreeFilesAndFolders(&content);
323 return dir;
324 }
325
RefreshDirContent(std::string path)326 void MtpFsDevice::RefreshDirContent(std::string path)
327 {
328 if (!rootDir_.IsFetched()) {
329 for (LIBMTP_devicestorage_t *s = device_->storage; s; s = s->next) {
330 rootDir_.AddDir(MtpFsTypeDir(rootNode_, 0, s->id, std::string(s->StorageDescription)));
331 rootDir_.SetFetched();
332 }
333 }
334 if (rootDir_.DirCount() == DIR_COUNT_ONE) {
335 path = '/' + rootDir_.Dirs().begin()->Name() + path;
336 }
337 if (path == "/") {
338 return;
339 }
340
341 std::string member;
342 std::istringstream ss(path);
343 MtpFsTypeDir *dir = &rootDir_;
344 while (std::getline(ss, member, '/')) {
345 if (member.empty()) {
346 LOGI("member is empty");
347 continue;
348 }
349 const MtpFsTypeDir *tmp = dir->Dir(member);
350 if (!tmp) {
351 LOGE("tmp is nullptr");
352 return;
353 }
354 dir = const_cast<MtpFsTypeDir *>(tmp);
355 }
356
357 uint32_t *out = (uint32_t *)malloc(sizeof(uint32_t));
358 if (!out) {
359 LOGE("malloc failed");
360 return;
361 }
362 CriticalEnter();
363 int32_t num = LIBMTP_Get_Children(device_, dir->StorageId(), dir->Id(), &out);
364 CriticalLeave();
365 free(out);
366 LOGI("LIBMTP_Get_Children path=%{public}s, num=%{public}d", path.c_str(), num);
367 if (num > FETCH_NUM && dir->IsFetched()) {
368 LOGW("LIBMTP_Get_Children path content num over 3000 and is fetched, no need to update");
369 return;
370 }
371
372 CriticalEnter();
373 dir->SetFetched();
374 LIBMTP_file_t *content = LIBMTP_Get_Files_And_Folders(device_, dir->StorageId(), dir->Id());
375 CriticalLeave();
376 HandleDirByFetch(content, dir);
377 LIBMTPFreeFilesAndFolders(&content);
378 LOGI("LIBMTP_Get_Files_And_Folders fetchcontent end");
379 }
380
GetFormattedTimestamp()381 static uint64_t GetFormattedTimestamp()
382 {
383 const int64_t secFactor = 1000;
384 auto now = std::chrono::system_clock::now();
385 auto millisecs = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch());
386 uint64_t milliSeconds = static_cast<uint64_t>(millisecs.count() / secFactor);
387 return milliSeconds;
388 }
389
DirCreateNew(const std::string & path)390 int MtpFsDevice::DirCreateNew(const std::string &path)
391 {
392 const std::string tmpBaseName(SmtpfsBaseName(path));
393 const std::string tmpDirName(SmtpfsDirName(path));
394 const MtpFsTypeDir *dirParent = DirFetchContent(tmpDirName);
395 if (!dirParent || dirParent->Id() == 0) {
396 LOGE("Can not remove directory: %{public}s", path.c_str());
397 return -EINVAL;
398 }
399 char *cName = strdup(tmpBaseName.c_str());
400 CriticalEnter();
401 uint32_t newId = LIBMTP_Create_Folder(device_, cName, dirParent->Id(), dirParent->StorageId());
402 CriticalLeave();
403 if (newId == 0) {
404 LOGE("Could not create directory: %{public}s", path.c_str());
405 LIBMTP_Dump_Errorstack(device_);
406 LIBMTP_Clear_Errorstack(device_);
407 } else {
408 MtpFsTypeDir dirToUpload(newId, dirParent->Id(), dirParent->StorageId(), tmpBaseName);
409 uint64_t time = GetFormattedTimestamp();
410 dirToUpload.SetModificationDate(time);
411 const_cast<MtpFsTypeDir *>(dirParent)->SetModificationDate(time);
412 const_cast<MtpFsTypeDir *>(dirParent)->AddDir(dirToUpload);
413 LOGI("Directory %{public}s created", path.c_str());
414 }
415 if (cName) {
416 free(static_cast<void *>(cName));
417 }
418 return newId != 0 ? 0 : -EINVAL;
419 }
420
DirRemove(const std::string & path)421 int MtpFsDevice::DirRemove(const std::string &path)
422 {
423 const std::string tmpBaseName(SmtpfsBaseName(path));
424 const std::string tmpDirName(SmtpfsDirName(path));
425 const MtpFsTypeDir *dirParent = DirFetchContent(tmpDirName);
426 const MtpFsTypeDir *dirToRemove = dirParent ? dirParent->Dir(tmpBaseName) : nullptr;
427 if (!dirParent || !dirToRemove || dirParent->Id() == 0) {
428 LOGE("No such directory %{public}s to remove", path.c_str());
429 return -ENOENT;
430 }
431 if (!dirToRemove->IsEmpty()) {
432 LOGE("Directory %{public}s is not empty", path.c_str());
433 return -ENOTEMPTY;
434 }
435 CriticalEnter();
436 int rval = LIBMTP_Delete_Object(device_, dirToRemove->Id());
437 CriticalLeave();
438 if (rval != 0) {
439 LOGE("Could not remove the directory: %{public}s", path.c_str());
440 LIBMTP_Dump_Errorstack(device_);
441 LIBMTP_Clear_Errorstack(device_);
442 return -EINVAL;
443 }
444 uint64_t time = GetFormattedTimestamp();
445 const_cast<MtpFsTypeDir *>(dirParent)->SetModificationDate(time);
446 const_cast<MtpFsTypeDir *>(dirParent)->RemoveDir(*dirToRemove);
447 LOGI("Folder %{public}s removed", path.c_str());
448 return 0;
449 }
450
DirReName(const std::string & oldPath,const std::string & newPath)451 int MtpFsDevice::DirReName(const std::string &oldPath, const std::string &newPath)
452 {
453 const std::string tmpOldBaseName(SmtpfsBaseName(oldPath));
454 const std::string tmpOldDirName(SmtpfsDirName(oldPath));
455 const std::string tmpNewBaseName(SmtpfsBaseName(newPath));
456 const std::string tmpNewDirName(SmtpfsDirName(newPath));
457 const MtpFsTypeDir *dirParent = DirFetchContent(tmpOldDirName);
458 const MtpFsTypeDir *dirToReName = dirParent ? dirParent->Dir(tmpOldBaseName) : nullptr;
459 if (!dirParent || !dirToReName || dirParent->Id() == 0) {
460 LOGE("Can not rename %{public}s to %{public}s ", tmpOldBaseName.c_str(), tmpNewBaseName.c_str());
461 return -EINVAL;
462 }
463 if (tmpOldDirName != tmpNewDirName) {
464 LOGE("Can not move %{public}s to %{public}s", oldPath.c_str(), newPath.c_str());
465 return -EINVAL;
466 }
467
468 LIBMTP_folder_t *folder = dirToReName->ToLIBMTPFolder();
469 CriticalEnter();
470 int ret = LIBMTP_Set_Folder_Name(device_, folder, tmpNewBaseName.c_str());
471 CriticalLeave();
472 free(static_cast<void *>(folder->name));
473 free(static_cast<void *>(folder));
474 if (ret != 0) {
475 LOGE("Could not rename %{public}s to %{public}s", oldPath.c_str(), tmpNewBaseName.c_str());
476 LIBMTP_Dump_Errorstack(device_);
477 LIBMTP_Clear_Errorstack(device_);
478 return -EINVAL;
479 }
480 const_cast<MtpFsTypeDir *>(dirToReName)->SetName(tmpNewBaseName);
481 LOGI("Directory %{public}s renamed to %{public}s", oldPath.c_str(), tmpNewBaseName.c_str());
482 return 0;
483 }
484
ReNameInner(const std::string & oldPath,const std::string & newPath)485 int MtpFsDevice::ReNameInner(const std::string &oldPath, const std::string &newPath)
486 {
487 const std::string tmpOldBaseName(SmtpfsBaseName(oldPath));
488 const std::string tmpOldDirName(SmtpfsDirName(oldPath));
489 const std::string tmpNewBaseName(SmtpfsBaseName(newPath));
490 const std::string tmpNewDirName(SmtpfsDirName(newPath));
491 const MtpFsTypeDir *dirOldParent = DirFetchContent(tmpOldDirName);
492 const MtpFsTypeDir *dirNewParent = DirFetchContent(tmpNewDirName);
493 const MtpFsTypeDir *dirToReName = dirOldParent ? dirOldParent->Dir(tmpOldBaseName) : nullptr;
494 const MtpFsTypeFile *fileToReName = dirOldParent ? dirOldParent->File(tmpOldBaseName) : nullptr;
495
496 LOGD("dirToReName:%{public}s", dirToReName);
497 LOGD("fileToReName:%{public}s", fileToReName);
498
499 if (!dirOldParent || !dirNewParent || dirOldParent->Id() == 0) {
500 return -EINVAL;
501 }
502 const MtpFsTypeBasic *objectToReName = dirToReName ? static_cast<const MtpFsTypeBasic *>(dirToReName) :
503 static_cast<const MtpFsTypeBasic *>(fileToReName);
504
505 if (!objectToReName) {
506 LOGE("No such file or directory to rename/move!");
507 return -ENOENT;
508 }
509
510 LOGD("objectToReName:%{public}s", objectToReName);
511 LOGD("objectToReName->id:%{public}d", objectToReName->Id());
512
513 if (tmpOldDirName != tmpNewDirName) {
514 CriticalEnter();
515 int rval =
516 LIBMTP_Set_Object_u32(device_, objectToReName->Id(), LIBMTP_PROPERTY_ParentObject, dirNewParent->Id());
517 CriticalLeave();
518 if (rval != 0) {
519 LOGE("Could not move %{public}s to %{public}s", oldPath.c_str(), newPath.c_str());
520 LIBMTP_Dump_Errorstack(device_);
521 LIBMTP_Clear_Errorstack(device_);
522 return -EINVAL;
523 }
524 const_cast<MtpFsTypeBasic *>(objectToReName)->SetParent(dirNewParent->Id());
525 }
526 if (tmpOldBaseName != tmpNewBaseName) {
527 CriticalEnter();
528 int rval =
529 LIBMTP_Set_Object_String(device_, objectToReName->Id(), LIBMTP_PROPERTY_Name, tmpNewBaseName.c_str());
530 CriticalLeave();
531 if (rval != 0) {
532 LOGE("Could not rename %{public}s to %{public}s", oldPath.c_str(), newPath.c_str());
533 LIBMTP_Dump_Errorstack(device_);
534 LIBMTP_Clear_Errorstack(device_);
535 return -EINVAL;
536 }
537 }
538 return 0;
539 }
540
ReName(const std::string & oldPath,const std::string & newPath)541 int MtpFsDevice::ReName(const std::string &oldPath, const std::string &newPath)
542 {
543 #ifndef SMTPFS_MOVE_BY_SET_OBJECT_PROPERTY
544 const std::string tmpOldBaseName(SmtpfsBaseName(oldPath));
545 const std::string tmpOldDirName(SmtpfsDirName(oldPath));
546 const std::string tmpNewDirName(SmtpfsDirName(newPath));
547 if (tmpOldDirName != tmpNewDirName) {
548 return -EINVAL;
549 }
550
551 const MtpFsTypeDir *dirParent = DirFetchContent(tmpOldDirName);
552 if (!dirParent || dirParent->Id() == 0) {
553 return -EINVAL;
554 }
555 const MtpFsTypeDir *dirToReName = dirParent->Dir(tmpOldBaseName);
556 int32_t cnt = 8;
557 while (cnt > 0) {
558 int32_t ret = LIBMTP_Read_Event_Async(device_, Libmtp_event_cb_fn, nullptr);
559 LOGI("File or dir rename regist event receiver, ret=%{public}d", ret);
560 cnt--;
561 }
562 if (dirToReName) {
563 return DirReName(oldPath, newPath);
564 } else {
565 return FileRename(oldPath, newPath);
566 }
567 #else
568 int32_t ret = ReNameInner(oldPath, newPath);
569 if (ret != 0) {
570 LOGE("ReNameInner fail");
571 return ret;
572 }
573 return 0;
574 #endif
575 }
576
FileRead(const std::string & path,char * buf,size_t size,off_t offset)577 int MtpFsDevice::FileRead(const std::string &path, char *buf, size_t size, off_t offset)
578 {
579 const std::string pathBaseName(SmtpfsBaseName(path));
580 const std::string pathDirName(SmtpfsDirName(path));
581 const MtpFsTypeDir *dirParent = DirFetchContent(pathDirName);
582 const MtpFsTypeFile *fileToFetch = dirParent ? dirParent->File(pathBaseName) : nullptr;
583 if (!dirParent) {
584 LOGE("Can not fetch %{public}s", path.c_str());
585 return -EINVAL;
586 }
587 if (!fileToFetch) {
588 LOGE("No such file %{public}s", path.c_str());
589 return -ENOENT;
590 }
591
592 // all systems clear
593 unsigned char *tmpBuf;
594 unsigned int tmpSize;
595 int rval = LIBMTP_GetPartialObject(device_, fileToFetch->Id(), offset, size, &tmpBuf, &tmpSize);
596 if (tmpSize > 0) {
597 if (memcpy_s(buf, tmpSize, tmpBuf, tmpSize) != EOK) {
598 LOGE("memcpy_s tmpBuf fail");
599 }
600 free(tmpBuf);
601 }
602
603 if (rval != 0) {
604 return -EIO;
605 }
606 return tmpSize;
607 }
608
FileWrite(const std::string & path,const char * buf,size_t size,off_t offset)609 int MtpFsDevice::FileWrite(const std::string &path, const char *buf, size_t size, off_t offset)
610 {
611 const std::string pathBaseName(SmtpfsBaseName(path));
612 const std::string pathDirName(SmtpfsDirName(path));
613 const MtpFsTypeDir *dirParent = DirFetchContent(pathDirName);
614 const MtpFsTypeFile *fileToFetch = dirParent ? dirParent->File(pathBaseName) : nullptr;
615 if (!dirParent) {
616 LOGE("Can not fetch %{public}s", path.c_str());
617 return -EINVAL;
618 }
619 if (!fileToFetch) {
620 LOGE("No such file %{public}s", path.c_str());
621 return -ENOENT;
622 }
623
624 // all systems clear
625 char *tmp = const_cast<char *>(buf);
626 int rval = LIBMTP_SendPartialObject(device_, fileToFetch->Id(), offset,
627 reinterpret_cast<unsigned char *>(tmp), size);
628 if (rval < 0) {
629 return -EIO;
630 }
631 return size;
632 }
633
FilePull(const std::string & src,const std::string & dst)634 int MtpFsDevice::FilePull(const std::string &src, const std::string &dst)
635 {
636 const std::string srcBaseName(SmtpfsBaseName(src));
637 const std::string srcDirName(SmtpfsDirName(src));
638 const MtpFsTypeDir *dirParent = DirFetchContent(srcDirName);
639 const MtpFsTypeFile *fileToFetch = dirParent ? dirParent->File(srcBaseName) : nullptr;
640 if (!dirParent) {
641 LOGE("Can not fetch %{public}s", src.c_str());
642 return -EINVAL;
643 }
644 if (!fileToFetch) {
645 LOGE("No such file %{public}s", src.c_str());
646 return -ENOENT;
647 }
648 if (fileToFetch->Size() == 0) {
649 int fd = ::creat(dst.c_str(), S_IRUSR | S_IWUSR);
650 ::close(fd);
651 } else {
652 LOGI("Started fetching %{public}s", src.c_str());
653 CriticalEnter();
654 int rval = LIBMTP_Get_File_To_File(device_, fileToFetch->Id(), dst.c_str(), nullptr, nullptr);
655 CriticalLeave();
656 if (rval != 0) {
657 LOGE("Could not fetch file %{public}s", src.c_str());
658 LIBMTP_Dump_Errorstack(device_);
659 LIBMTP_Clear_Errorstack(device_);
660 return -ENOENT;
661 }
662 }
663 LOGI("File fetched %{public}s", src.c_str());
664 return 0;
665 }
666
FilePush(const std::string & src,const std::string & dst)667 int MtpFsDevice::FilePush(const std::string &src, const std::string &dst)
668 {
669 const std::string dstBaseName(SmtpfsBaseName(dst));
670 const std::string dstDirName(SmtpfsDirName(dst));
671 const MtpFsTypeDir *dirParent = DirFetchContent(dstDirName);
672 const MtpFsTypeFile *fileToRemove = dirParent ? dirParent->File(dstBaseName) : nullptr;
673 if (!dirParent) {
674 LOGE("Can not fetch %{public}s", dst.c_str());
675 return -EINVAL;
676 }
677 if (fileToRemove) {
678 CriticalEnter();
679 int rval = LIBMTP_Delete_Object(device_, fileToRemove->Id());
680 CriticalLeave();
681 if (rval != 0) {
682 LOGE("Can not upload %{public}s to %{public}s", src.c_str(), dst.c_str());
683 return -EINVAL;
684 }
685 }
686
687 struct stat fileStat;
688 stat(src.c_str(), &fileStat);
689 MtpFsTypeFile fileToUpload(0, dirParent->Id(), dirParent->StorageId(), dstBaseName,
690 static_cast<uint64_t>(fileStat.st_size), 0);
691 LIBMTP_file_t *f = fileToUpload.ToLIBMTPFile();
692 if (fileStat.st_size) {
693 LOGI("Started uploading %{public}s", dst.c_str());
694 }
695 CriticalEnter();
696 int rval = LIBMTP_Send_File_From_File(device_, src.c_str(), f, nullptr, nullptr);
697 CriticalLeave();
698 if (rval != 0) {
699 LOGE("Could not upload file %{public}s", src.c_str());
700 LIBMTP_Dump_Errorstack(device_);
701 LIBMTP_Clear_Errorstack(device_);
702 rval = -EINVAL;
703 } else {
704 fileToUpload.SetId(f->item_id);
705 fileToUpload.SetParent(f->parent_id);
706 fileToUpload.SetStorage(f->storage_id);
707 fileToUpload.SetName(std::string(f->filename));
708 fileToUpload.SetModificationDate(fileStat.st_mtime);
709 if (fileToRemove) {
710 const_cast<MtpFsTypeDir *>(dirParent)->ReplaceFile(*fileToRemove, fileToUpload);
711 } else {
712 const_cast<MtpFsTypeDir *>(dirParent)->AddFile(fileToUpload);
713 }
714 }
715 free(static_cast<void *>(f->filename));
716 free(static_cast<void *>(f));
717 return rval;
718 }
719
FileRemove(const std::string & path)720 int MtpFsDevice::FileRemove(const std::string &path)
721 {
722 const std::string tmpBaseName(SmtpfsBaseName(path));
723 const std::string tmpDirName(SmtpfsDirName(path));
724 const MtpFsTypeDir *dirParent = DirFetchContent(tmpDirName);
725 const MtpFsTypeFile *fileToRemove = dirParent ? dirParent->File(tmpBaseName) : nullptr;
726 if (!dirParent || !fileToRemove) {
727 LOGE("No such file %{public}s to remove", path.c_str());
728 return -ENOENT;
729 }
730 CriticalEnter();
731 int rval = LIBMTP_Delete_Object(device_, fileToRemove->Id());
732 CriticalLeave();
733 if (rval != 0) {
734 LOGE("Could not remove the directory %{public}s", path.c_str());
735 return -EINVAL;
736 }
737 uint64_t time = GetFormattedTimestamp();
738 const_cast<MtpFsTypeDir *>(dirParent)->SetModificationDate(time);
739 const_cast<MtpFsTypeDir *>(dirParent)->RemoveFile(*fileToRemove);
740 LOGI("File %{public}s removed", path.c_str());
741 return 0;
742 }
743
FileRename(const std::string & oldPath,const std::string & newPath)744 int MtpFsDevice::FileRename(const std::string &oldPath, const std::string &newPath)
745 {
746 const std::string tmpOldBaseName(SmtpfsBaseName(oldPath));
747 const std::string tmpOldDirName(SmtpfsDirName(oldPath));
748 const std::string tmpNewBaseName(SmtpfsBaseName(newPath));
749 const std::string tmpNewDirName(SmtpfsDirName(newPath));
750 const MtpFsTypeDir *dirParent = DirFetchContent(tmpOldDirName);
751 const MtpFsTypeFile *fileToReName = dirParent ? dirParent->File(tmpOldBaseName) : nullptr;
752 if (!dirParent || !fileToReName || tmpOldDirName != tmpNewDirName) {
753 LOGE("Can not rename %{public}s to %{public}s", oldPath.c_str(), tmpNewBaseName.c_str());
754 return -EINVAL;
755 }
756
757 LIBMTP_file_t *file = fileToReName->ToLIBMTPFile();
758 CriticalEnter();
759 int rval = LIBMTP_Set_File_Name(device_, file, tmpNewBaseName.c_str());
760 CriticalLeave();
761 free(static_cast<void *>(file->filename));
762 free(static_cast<void *>(file));
763 if (rval > 0) {
764 LOGE("Could not rename %{public}s to %{public}s", oldPath.c_str(), newPath.c_str());
765 LIBMTP_Dump_Errorstack(device_);
766 LIBMTP_Clear_Errorstack(device_);
767 return -EINVAL;
768 }
769 const_cast<MtpFsTypeFile *>(fileToReName)->SetName(tmpNewBaseName);
770 LOGI("File %{public}s renamed to %{public}s", oldPath.c_str(), tmpNewBaseName.c_str());
771 return 0;
772 }
773
GetCapabilities() const774 MtpFsDevice::Capabilities MtpFsDevice::GetCapabilities() const
775 {
776 return capabilities_;
777 }
778
GetCapabilities(const MtpFsDevice & device)779 MtpFsDevice::Capabilities MtpFsDevice::GetCapabilities(const MtpFsDevice &device)
780 {
781 MtpFsDevice::Capabilities capabilities;
782 #ifdef HAVE_LIBMTP_CHECK_CAPABILITY
783 if (device.device_) {
784 capabilities.SetCanGetPartialObject(
785 static_cast<bool>(LIBMTP_Check_Capability(device.device_, LIBMTP_DEVICECAP_GetPartialObject)));
786 capabilities.SetCanSendPartialobject(
787 static_cast<bool>(LIBMTP_Check_Capability(device.device_, LIBMTP_DEVICECAP_SendPartialObject)));
788 capabilities.SetCanEditObjects(
789 static_cast<bool>(LIBMTP_Check_Capability(device.device_, LIBMTP_DEVICECAP_EditObjects)));
790 }
791 #endif
792 return capabilities;
793 }
794
AddUploadRecord(const std::string path,bool value)795 void MtpFsDevice::AddUploadRecord(const std::string path, bool value)
796 {
797 std::unique_lock<std::mutex> lock(uploadRecordMutex_);
798 auto it = uploadRecordMap_.find(path);
799 if (it == uploadRecordMap_.end()) {
800 LOGI("uploadRecordMap_ add %{public}s to %{public}d", path.c_str(), value);
801 uploadRecordMap_[path] = value;
802 return;
803 }
804
805 LOGI("uploadRecordMap_ set path %{public}s to %{public}d", path.c_str(), value);
806 it->second = value;
807 return;
808 }
809
RemoveUploadRecord(const std::string path)810 void MtpFsDevice::RemoveUploadRecord(const std::string path)
811 {
812 std::unique_lock<std::mutex> lock(uploadRecordMutex_);
813 auto it = uploadRecordMap_.find(path);
814 if (it == uploadRecordMap_.end()) {
815 LOGE("uploadRecordMap_ not contain %{public}s", path.c_str());
816 return;
817 }
818
819 LOGI("uploadRecordMap_ remove %{public}s", path.c_str());
820 uploadRecordMap_.erase(it);
821 return;
822 }
823
SetUploadRecord(const std::string path,bool value)824 void MtpFsDevice::SetUploadRecord(const std::string path, bool value)
825 {
826 std::unique_lock<std::mutex> lock(uploadRecordMutex_);
827 auto it = uploadRecordMap_.find(path);
828 if (it == uploadRecordMap_.end()) {
829 LOGE("uploadRecordMap_ not contain %{public}s", path.c_str());
830 return;
831 }
832
833 LOGI("uploadRecordMap_ set path %{public}s to %{public}d", path.c_str(), value);
834 it->second = value;
835 return;
836 }
837
FindUploadRecord(const std::string path)838 std::tuple<std::string, bool> MtpFsDevice::FindUploadRecord(const std::string path)
839 {
840 std::unique_lock<std::mutex> lock(uploadRecordMutex_);
841 auto it = uploadRecordMap_.find(path);
842 if (it == uploadRecordMap_.end()) {
843 LOGE("uploadRecordMap_ not contain %{public}s", path.c_str());
844 return {"", false};
845 }
846 return {it->first, it->second};
847 }