• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 }