1 /* ***** BEGIN LICENSE BLOCK ***** 2 * Copyright (C) 2012-2016, Peter Hatina <phatina@gmail.com> 3 * 4 * This program is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU General Public License as 6 * published by the Free Software Foundation; either version 2 of 7 * the License, or (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program. If not, see <http://www.gnu.org/licenses/>. 16 * ***** END LICENSE BLOCK ***** */ 17 18 #include <config.h> 19 #include <algorithm> 20 #include <sstream> 21 #include <vector> 22 #include <cstring> 23 #include <cstdint> 24 #include <cstdlib> 25 extern "C" { 26 # include <unistd.h> 27 # include <sys/types.h> 28 # define _DARWIN_USE_64_BIT_INODE 29 # include <sys/stat.h> 30 } 31 #include "simple-mtpfs-fuse.h" 32 #include "simple-mtpfs-libmtp.h" 33 #include "simple-mtpfs-log.h" 34 #include "simple-mtpfs-mtp-device.h" 35 #include "simple-mtpfs-util.h" 36 37 uint32_t MTPDevice::s_root_node = ~0; 38 39 MTPDevice::MTPDevice(): 40 m_device(nullptr), 41 m_capabilities(), 42 m_device_mutex(), 43 m_root_dir(), 44 m_move_enabled(false) 45 { 46 StreamHelper::off(); 47 LIBMTP_Init(); 48 StreamHelper::on(); 49 } 50 51 MTPDevice::~MTPDevice() 52 { 53 disconnect(); 54 } 55 56 bool MTPDevice::connect(LIBMTP_raw_device_t *dev) 57 { 58 if (m_device) { 59 logerr("Already connected.\n"); 60 return true; 61 } 62 63 // Do not output LIBMTP debug stuff 64 StreamHelper::off(); 65 m_device = LIBMTP_Open_Raw_Device_Uncached(dev); 66 StreamHelper::on(); 67 68 if (!m_device) { 69 LIBMTP_Dump_Errorstack(m_device); 70 return false; 71 } 72 73 if (!enumStorages()) 74 return false; 75 76 // Retrieve capabilities. 77 m_capabilities = MTPDevice::getCapabilities(*this); 78 79 logmsg("Connected.\n"); 80 return true; 81 } 82 83 bool MTPDevice::connect_priv(int dev_no, const std::string &dev_file) 84 { 85 if (m_device) { 86 logerr("Already connected.\n"); 87 return true; 88 } 89 90 int raw_devices_cnt; 91 LIBMTP_raw_device_t *raw_devices; 92 93 // Do not output LIBMTP debug stuff 94 StreamHelper::off(); 95 LIBMTP_error_number_t err = LIBMTP_Detect_Raw_Devices( 96 &raw_devices, &raw_devices_cnt); 97 StreamHelper::on(); 98 99 if (err != LIBMTP_ERROR_NONE) { 100 switch(err) { 101 case LIBMTP_ERROR_NO_DEVICE_ATTACHED: 102 logerr("No raw devices found.\n"); 103 break; 104 case LIBMTP_ERROR_CONNECTING: 105 logerr("There has been an error connecting. Exiting.\n"); 106 break; 107 case LIBMTP_ERROR_MEMORY_ALLOCATION: 108 logerr("Encountered a Memory Allocation Error. Exiting.\n"); 109 break; 110 case LIBMTP_ERROR_GENERAL: 111 logerr("General error occurred. Exiting.\n"); 112 break; 113 case LIBMTP_ERROR_USB_LAYER: 114 logerr("USB Layer error occurred. Exiting.\n"); 115 break; 116 default: 117 break; 118 } 119 return false; 120 } 121 122 #ifndef HAVE_LIBUSB1 123 if (!dev_file.empty()) { 124 uint8_t bnum, dnum; 125 dev_no = raw_devices_cnt; 126 127 if (smtpfs_usb_devpath(dev_file, &bnum, &dnum)) 128 for (dev_no = 0; dev_no < raw_devices_cnt; ++dev_no) 129 if (bnum == raw_devices[dev_no].bus_location && 130 dnum == raw_devices[dev_no].devnum) 131 break; 132 133 if (dev_no == raw_devices_cnt) { 134 logerr("Can not open such device '", dev_file, "'.\n"); 135 free(static_cast<void*>(raw_devices)); 136 return false; 137 } 138 } 139 #endif // !HAVE_LIBUSB1 140 141 if (dev_no < 0 || dev_no >= raw_devices_cnt) { 142 logerr("Can not connect to device no. ", dev_no + 1, ".\n"); 143 free(static_cast<void*>(raw_devices)); 144 return false; 145 } 146 147 LIBMTP_raw_device_t *raw_device = &raw_devices[dev_no]; 148 149 // Do not output LIBMTP debug stuff 150 StreamHelper::off(); 151 m_device = LIBMTP_Open_Raw_Device_Uncached(raw_device); 152 StreamHelper::on(); 153 free(static_cast<void*>(raw_devices)); 154 155 if (!m_device) { 156 LIBMTP_Dump_Errorstack(m_device); 157 return false; 158 } 159 160 if (!enumStorages()) 161 return false; 162 163 // Retrieve capabilities. 164 m_capabilities = MTPDevice::getCapabilities(*this); 165 166 logmsg("Connected.\n"); 167 return true; 168 } 169 170 bool MTPDevice::connect(int dev_no) 171 { 172 return connect_priv(dev_no, std::string()); 173 } 174 175 #ifdef HAVE_LIBUSB1 176 bool MTPDevice::connect(const std::string &dev_file) 177 { 178 if (m_device) { 179 logerr("Already connected.\n"); 180 return true; 181 } 182 183 LIBMTP_raw_device_t *raw_device = smtpfs_raw_device_new(dev_file); 184 if (!raw_device) { 185 logerr("Can not open such device '", dev_file, "'.\n"); 186 return false; 187 } 188 189 bool rval = connect(raw_device); 190 191 // TODO: Smart pointer with alloc, free hooks. 192 smtpfs_raw_device_free(raw_device); 193 194 return rval; 195 } 196 #else 197 bool MTPDevice::connect(const std::string &dev_file) 198 { 199 return connect_priv(-1, dev_file); 200 } 201 #endif 202 203 void MTPDevice::disconnect() 204 { 205 if (!m_device) 206 return; 207 208 LIBMTP_Release_Device(m_device); 209 m_device = nullptr; 210 logmsg("Disconnected.\n"); 211 } 212 213 uint64_t MTPDevice::storageTotalSize() const 214 { 215 uint64_t total = 0; 216 for (LIBMTP_devicestorage_t *s = m_device->storage; s; s = s->next) 217 total += s->MaxCapacity; 218 return total; 219 } 220 221 uint64_t MTPDevice::storageFreeSize() const 222 { 223 uint64_t free = 0; 224 for (LIBMTP_devicestorage_t *s = m_device->storage; s; s = s->next) 225 free += s->FreeSpaceInBytes; 226 return free; 227 } 228 229 bool MTPDevice::enumStorages() 230 { 231 criticalEnter(); 232 LIBMTP_Clear_Errorstack(m_device); 233 if (LIBMTP_Get_Storage(m_device, LIBMTP_STORAGE_SORTBY_NOTSORTED) < 0) { 234 std::cerr << "Could not retrieve device storage.\n"; 235 std::cerr << "For android phones make sure the screen is unlocked.\n"; 236 logerr("Could not retrieve device storage. Exiting.\n"); 237 LIBMTP_Dump_Errorstack(m_device); 238 LIBMTP_Clear_Errorstack(m_device); 239 return false; 240 } 241 criticalLeave(); 242 return true; 243 } 244 245 const TypeDir *MTPDevice::dirFetchContent(std::string path) 246 { 247 if (!m_root_dir.isFetched()) { 248 for (LIBMTP_devicestorage_t *s = m_device->storage; s; s = s->next) { 249 m_root_dir.addDir(TypeDir(s_root_node, 0, s->id, 250 std::string(s->StorageDescription))); 251 m_root_dir.setFetched(); 252 } 253 } 254 255 if (m_root_dir.dirCount() == 1) 256 path = '/' + m_root_dir.dirs().begin()->name() + path; 257 258 if (path == "/") 259 return &m_root_dir; 260 261 std::string member; 262 std::istringstream ss(path); 263 TypeDir *dir = &m_root_dir; 264 while (std::getline(ss, member, '/')) { 265 if (member.empty()) 266 continue; 267 268 const TypeDir *tmp = dir->dir(member); 269 if (!tmp && !dir->isFetched()) { 270 criticalEnter(); 271 LIBMTP_file_t *content = LIBMTP_Get_Files_And_Folders( 272 m_device, dir->storageid(), dir->id()); 273 criticalLeave(); 274 for (LIBMTP_file_t *f = content; f; f = f->next) { 275 if (f->filetype == LIBMTP_FILETYPE_FOLDER) 276 dir->addDir(TypeDir(f)); 277 else 278 dir->addFile(TypeFile(f)); 279 } 280 LIBMTP_Free_Files_And_Folders(&content); 281 dir->setFetched(); 282 tmp = dir->dir(member); 283 } 284 285 if (!tmp) 286 return nullptr; 287 dir = const_cast<TypeDir*>(tmp); 288 } 289 290 if (dir->isFetched()) 291 return dir; 292 293 criticalEnter(); 294 dir->setFetched(); 295 LIBMTP_file_t *content = LIBMTP_Get_Files_And_Folders( 296 m_device, dir->storageid(), dir->id()); 297 criticalLeave(); 298 for (LIBMTP_file_t *f = content; f; f = f->next) { 299 if (f->filetype == LIBMTP_FILETYPE_FOLDER) 300 dir->addDir(TypeDir(f)); 301 else 302 dir->addFile(TypeFile(f)); 303 } 304 LIBMTP_Free_Files_And_Folders(&content); 305 return dir; 306 } 307 308 int MTPDevice::dirCreateNew(const std::string &path) 309 { 310 const std::string tmp_basename(smtpfs_basename(path)); 311 const std::string tmp_dirname(smtpfs_dirname(path)); 312 const TypeDir *dir_parent = dirFetchContent(tmp_dirname); 313 if (!dir_parent || dir_parent->id() == 0) { 314 logerr("Can not remove directory '", path, "'.\n"); 315 return -EINVAL; 316 } 317 char *c_name = strdup(tmp_basename.c_str()); 318 criticalEnter(); 319 uint32_t new_id = LIBMTP_Create_Folder(m_device, c_name, 320 dir_parent->id(), dir_parent->storageid()); 321 criticalLeave(); 322 if (new_id == 0) { 323 logerr("Could not create directory '", path, "'.\n"); 324 LIBMTP_Dump_Errorstack(m_device); 325 LIBMTP_Clear_Errorstack(m_device); 326 } else { 327 const_cast<TypeDir*>(dir_parent)->addDir(TypeDir(new_id, dir_parent->id(), 328 dir_parent->storageid(), tmp_basename)); 329 logmsg("Directory '", path, "' created.\n"); 330 } 331 free(static_cast<void*>(c_name)); 332 return new_id != 0 ? 0 : -EINVAL; 333 } 334 335 int MTPDevice::dirRemove(const std::string &path) 336 { 337 const std::string tmp_basename(smtpfs_basename(path)); 338 const std::string tmp_dirname(smtpfs_dirname(path)); 339 const TypeDir *dir_parent = dirFetchContent(tmp_dirname); 340 const TypeDir *dir_to_remove = dir_parent ? dir_parent->dir(tmp_basename) : nullptr; 341 if (!dir_parent || !dir_to_remove || dir_parent->id() == 0) { 342 logerr("No such directory '", path, "' to remove.\n"); 343 return -ENOENT; 344 } 345 if (!dir_to_remove->isEmpty()) 346 return -ENOTEMPTY; 347 criticalEnter(); 348 int rval = LIBMTP_Delete_Object(m_device, dir_to_remove->id()); 349 criticalLeave(); 350 if (rval != 0){ 351 logerr("Could not remove the directory '", path, "'.\n"); 352 LIBMTP_Dump_Errorstack(m_device); 353 LIBMTP_Clear_Errorstack(m_device); 354 return -EINVAL; 355 } 356 const_cast<TypeDir*>(dir_parent)->removeDir(*dir_to_remove); 357 logmsg("Folder '", path, "' removed.\n"); 358 return 0; 359 } 360 361 int MTPDevice::dirRename(const std::string &oldpath, const std::string &newpath) 362 { 363 const std::string tmp_old_basename(smtpfs_basename(oldpath)); 364 const std::string tmp_old_dirname(smtpfs_dirname(oldpath)); 365 const std::string tmp_new_basename(smtpfs_basename(newpath)); 366 const std::string tmp_new_dirname(smtpfs_dirname(newpath)); 367 const TypeDir *dir_parent = dirFetchContent(tmp_old_dirname); 368 const TypeDir *dir_to_rename = dir_parent ? dir_parent->dir(tmp_old_basename) : nullptr; 369 if (!dir_parent || !dir_to_rename || dir_parent->id() == 0) { 370 logerr("Can not rename '", tmp_old_basename, "' to '", 371 tmp_new_basename, "'.\n"); 372 return -EINVAL; 373 } 374 if (tmp_old_dirname != tmp_new_dirname) { 375 logerr("Can not move '", oldpath, "' to '", newpath, "'.\n"); 376 return -EINVAL; 377 } 378 379 LIBMTP_folder_t *folder = dir_to_rename->toLIBMTPFolder(); 380 criticalEnter(); 381 int ret = LIBMTP_Set_Folder_Name(m_device, folder, tmp_new_basename.c_str()); 382 criticalLeave(); 383 free(static_cast<void*>(folder->name)); 384 free(static_cast<void*>(folder)); 385 if (ret != 0) { 386 logerr("Could not rename '", oldpath, "' to '", tmp_new_basename, "'.\n"); 387 LIBMTP_Dump_Errorstack(m_device); 388 LIBMTP_Clear_Errorstack(m_device); 389 return -EINVAL; 390 } 391 const_cast<TypeDir*>(dir_to_rename)->setName(tmp_new_basename); 392 logmsg("Directory '", oldpath, "' renamed to '", tmp_new_basename, "'.\n"); 393 return 0; 394 } 395 396 int MTPDevice::rename(const std::string &oldpath, const std::string &newpath) 397 { 398 #ifndef SMTPFS_MOVE_BY_SET_OBJECT_PROPERTY 399 const std::string tmp_old_basename(smtpfs_basename(oldpath)); 400 const std::string tmp_old_dirname(smtpfs_dirname(oldpath)); 401 const std::string tmp_new_dirname(smtpfs_dirname(newpath)); 402 if (tmp_old_dirname != tmp_new_dirname) 403 return -EINVAL; 404 405 const TypeDir *dir_parent = dirFetchContent(tmp_old_dirname); 406 if (!dir_parent || dir_parent->id() == 0) 407 return -EINVAL; 408 const TypeDir *dir_to_rename = dir_parent->dir(tmp_old_basename); 409 if (dir_to_rename) 410 return dirRename(oldpath, newpath); 411 else 412 return fileRename(oldpath, newpath); 413 #else 414 const std::string tmp_old_basename(smtpfs_basename(oldpath)); 415 const std::string tmp_old_dirname(smtpfs_dirname(oldpath)); 416 const std::string tmp_new_basename(smtpfs_basename(newpath)); 417 const std::string tmp_new_dirname(smtpfs_dirname(newpath)); 418 const TypeDir *dir_old_parent = dirFetchContent(tmp_old_dirname); 419 const TypeDir *dir_new_parent = dirFetchContent(tmp_new_dirname); 420 const TypeDir *dir_to_rename = dir_old_parent ? dir_old_parent->dir(tmp_old_basename) : nullptr; 421 const TypeFile *file_to_rename = dir_old_parent ? dir_old_parent->file(tmp_old_basename) : nullptr; 422 423 logdebug("dir_to_rename: ", dir_to_rename, "\n"); 424 logdebug("file_to_rename: ", file_to_rename, "\n"); 425 426 if (!dir_old_parent || !dir_new_parent || dir_old_parent->id() == 0) 427 return -EINVAL; 428 429 const TypeBasic *object_to_rename = dir_to_rename ? 430 static_cast<const TypeBasic*>(dir_to_rename) : 431 static_cast<const TypeBasic*>(file_to_rename); 432 433 logdebug("object_to_rename: ", object_to_rename, "\n"); 434 logdebug("object_to_rename->id(): ", object_to_rename->id(), "\n"); 435 436 if (!object_to_rename) { 437 logerr("No such file or directory to rename/move!\n"); 438 return -ENOENT; 439 } 440 441 if (tmp_old_dirname != tmp_new_dirname) { 442 criticalEnter(); 443 int rval = LIBMTP_Set_Object_u32(m_device, object_to_rename->id(), 444 LIBMTP_PROPERTY_ParentObject, dir_new_parent->id()); 445 criticalLeave(); 446 if (rval != 0) { 447 logerr("Could not move '", oldpath, "' to '", newpath, "'.\n"); 448 LIBMTP_Dump_Errorstack(m_device); 449 LIBMTP_Clear_Errorstack(m_device); 450 return -EINVAL; 451 } 452 const_cast<TypeBasic*>(object_to_rename)->setParent(dir_new_parent->id()); 453 } 454 if (tmp_old_basename != tmp_new_basename) { 455 criticalEnter(); 456 int rval = LIBMTP_Set_Object_String(m_device, object_to_rename->id(), 457 LIBMTP_PROPERTY_Name, tmp_new_basename.c_str()); 458 criticalLeave(); 459 if (rval != 0) { 460 logerr("Could not rename '", oldpath, "' to '", newpath, "'.\n"); 461 LIBMTP_Dump_Errorstack(m_device); 462 LIBMTP_Clear_Errorstack(m_device); 463 return -EINVAL; 464 } 465 } 466 return 0; 467 #endif 468 } 469 470 int MTPDevice::fileRead(const std::string &path, char *buf, size_t size, 471 off_t offset) 472 { 473 const std::string path_basename(smtpfs_basename(path)); 474 const std::string path_dirname(smtpfs_dirname(path)); 475 const TypeDir *dir_parent = dirFetchContent(path_dirname); 476 const TypeFile *file_to_fetch = dir_parent ? 477 dir_parent->file(path_basename) : nullptr; 478 if (!dir_parent) { 479 logerr("Can not fetch '", path, "'.\n"); 480 return -EINVAL; 481 } 482 if (!file_to_fetch) { 483 logerr("No such file '", path, "'.\n"); 484 return -ENOENT; 485 } 486 487 // all systems clear 488 unsigned char *tmp_buf; 489 unsigned int tmp_size; 490 int rval = LIBMTP_GetPartialObject(m_device, file_to_fetch->id(), 491 offset, size, &tmp_buf, &tmp_size); 492 if (tmp_size > 0) { 493 memcpy(buf, tmp_buf, tmp_size); 494 free(tmp_buf); 495 } 496 497 if (rval != 0) 498 return -EIO; 499 return tmp_size; 500 } 501 502 int MTPDevice::fileWrite(const std::string &path, const char *buf, size_t size, 503 off_t offset) 504 { 505 const std::string path_basename(smtpfs_basename(path)); 506 const std::string path_dirname(smtpfs_dirname(path)); 507 const TypeDir *dir_parent = dirFetchContent(path_dirname); 508 const TypeFile *file_to_fetch = dir_parent ? 509 dir_parent->file(path_basename) : nullptr; 510 if (!dir_parent) { 511 logerr("Can not fetch '", path, "'.\n"); 512 return -EINVAL; 513 } 514 if (!file_to_fetch) { 515 logerr("No such file '", path, "'.\n"); 516 return -ENOENT; 517 } 518 519 // all systems clear 520 int rval = LIBMTP_SendPartialObject(m_device, file_to_fetch->id(), 521 offset, (unsigned char *) buf, size); 522 523 if (rval < 0) 524 return -EIO; 525 return size; 526 } 527 528 int MTPDevice::filePull(const std::string &src, const std::string &dst) 529 { 530 const std::string src_basename(smtpfs_basename(src)); 531 const std::string src_dirname(smtpfs_dirname(src)); 532 const TypeDir *dir_parent = dirFetchContent(src_dirname); 533 const TypeFile *file_to_fetch = dir_parent ? dir_parent->file(src_basename) : nullptr; 534 if (!dir_parent) { 535 logerr("Can not fetch '", src, "'.\n"); 536 return -EINVAL; 537 } 538 if (!file_to_fetch) { 539 logerr("No such file '", src, "'.\n"); 540 return -ENOENT; 541 } 542 if (file_to_fetch->size() == 0) { 543 int fd = ::creat(dst.c_str(), S_IRUSR | S_IWUSR); 544 ::close(fd); 545 } else { 546 logmsg("Started fetching '", src, "'.\n"); 547 criticalEnter(); 548 int rval = LIBMTP_Get_File_To_File(m_device, file_to_fetch->id(), 549 dst.c_str(), nullptr, nullptr); 550 criticalLeave(); 551 if (rval != 0) { 552 logerr("Could not fetch file '", src, "'.\n"); 553 LIBMTP_Dump_Errorstack(m_device); 554 LIBMTP_Clear_Errorstack(m_device); 555 return -ENOENT; 556 } 557 } 558 logmsg("File fetched '", src, "'.\n"); 559 return 0; 560 } 561 562 int MTPDevice::filePush(const std::string &src, const std::string &dst) 563 { 564 const std::string dst_basename(smtpfs_basename(dst)); 565 const std::string dst_dirname(smtpfs_dirname(dst)); 566 const TypeDir *dir_parent = dirFetchContent(dst_dirname); 567 const TypeFile *file_to_remove = dir_parent ? dir_parent->file(dst_basename) : nullptr; 568 if (dir_parent && file_to_remove) { 569 criticalEnter(); 570 int rval = LIBMTP_Delete_Object(m_device, file_to_remove->id()); 571 criticalLeave(); 572 if (rval != 0) { 573 logerr("Can not upload '", src, "' to '", dst, "'.\n"); 574 return -EINVAL; 575 } 576 } 577 578 struct stat file_stat; 579 stat(src.c_str(), &file_stat); 580 TypeFile file_to_upload(0, dir_parent->id(), dir_parent->storageid(), 581 dst_basename, static_cast<uint64_t>(file_stat.st_size), 0); 582 LIBMTP_file_t *f = file_to_upload.toLIBMTPFile(); 583 if (file_stat.st_size) 584 logmsg("Started uploading '", dst, "'.\n"); 585 criticalEnter(); 586 int rval = LIBMTP_Send_File_From_File(m_device, src.c_str(), f, nullptr, nullptr); 587 criticalLeave(); 588 if (rval != 0) { 589 logerr("Could not upload file '", src, "'.\n"); 590 LIBMTP_Dump_Errorstack(m_device); 591 LIBMTP_Clear_Errorstack(m_device); 592 rval = -EINVAL; 593 } else { 594 file_to_upload.setId(f->item_id); 595 file_to_upload.setParent(f->parent_id); 596 file_to_upload.setStorage(f->storage_id); 597 file_to_upload.setName(std::string(f->filename)); 598 file_to_upload.setModificationDate(file_stat.st_mtime); 599 if (file_to_remove) 600 const_cast<TypeDir*>(dir_parent)->replaceFile(*file_to_remove, file_to_upload); 601 else 602 const_cast<TypeDir*>(dir_parent)->addFile(file_to_upload); 603 } 604 free(static_cast<void*>(f->filename)); 605 free(static_cast<void*>(f)); 606 logmsg("File '", dst, (file_stat.st_size ? " uploaded" : " created"), ".\n"); 607 return rval; 608 } 609 610 int MTPDevice::fileRemove(const std::string &path) 611 { 612 const std::string tmp_basename(smtpfs_basename(path)); 613 const std::string tmp_dirname(smtpfs_dirname(path)); 614 const TypeDir *dir_parent = dirFetchContent(tmp_dirname); 615 const TypeFile *file_to_remove = dir_parent ? dir_parent->file(tmp_basename) : nullptr; 616 if (!dir_parent || !file_to_remove) { 617 logerr("No such file '", path, "' to remove.\n"); 618 return -ENOENT; 619 } 620 criticalEnter(); 621 int rval = LIBMTP_Delete_Object(m_device, file_to_remove->id()); 622 criticalLeave(); 623 if (rval != 0) { 624 logerr("Could not remove the directory '", path, "'.\n"); 625 return -EINVAL; 626 } 627 const_cast<TypeDir*>(dir_parent)->removeFile(*file_to_remove); 628 logmsg("File '", path, "' removed.\n"); 629 return 0; 630 } 631 632 int MTPDevice::fileRename(const std::string &oldpath, const std::string &newpath) 633 { 634 const std::string tmp_old_basename(smtpfs_basename(oldpath)); 635 const std::string tmp_old_dirname(smtpfs_dirname(oldpath)); 636 const std::string tmp_new_basename(smtpfs_basename(newpath)); 637 const std::string tmp_new_dirname(smtpfs_dirname(newpath)); 638 const TypeDir *dir_parent = dirFetchContent(tmp_old_dirname); 639 const TypeFile *file_to_rename = dir_parent ? dir_parent->file(tmp_old_basename) : nullptr; 640 if (!dir_parent || !file_to_rename || tmp_old_dirname != tmp_new_dirname) { 641 logerr("Can not rename '", oldpath, "' to '", tmp_new_basename, "'.\n"); 642 return -EINVAL; 643 } 644 645 LIBMTP_file_t *file = file_to_rename->toLIBMTPFile(); 646 criticalEnter(); 647 int rval = LIBMTP_Set_File_Name(m_device, file, tmp_new_basename.c_str()); 648 criticalLeave(); 649 free(static_cast<void*>(file->filename)); 650 free(static_cast<void*>(file)); 651 if (rval > 0) { 652 logerr("Could not rename '", oldpath, "' to '", newpath, "'.\n"); 653 LIBMTP_Dump_Errorstack(m_device); 654 LIBMTP_Clear_Errorstack(m_device); 655 return -EINVAL; 656 } 657 const_cast<TypeFile*>(file_to_rename)->setName(tmp_new_basename); 658 logmsg("File '", oldpath, "' renamed to '", tmp_new_basename, "'.\n"); 659 return 0; 660 } 661 662 MTPDevice::Capabilities MTPDevice::getCapabilities() const 663 { 664 return m_capabilities; 665 } 666 667 MTPDevice::Capabilities MTPDevice::getCapabilities(const MTPDevice &device) 668 { 669 MTPDevice::Capabilities capabilities; 670 671 #ifdef HAVE_LIBMTP_CHECK_CAPABILITY 672 if (device.m_device) { 673 capabilities.setCanGetPartialObject( 674 static_cast<bool>( 675 LIBMTP_Check_Capability( 676 device.m_device, 677 LIBMTP_DEVICECAP_GetPartialObject))); 678 capabilities.setCanSendPartialobject( 679 static_cast<bool>( 680 LIBMTP_Check_Capability( 681 device.m_device, 682 LIBMTP_DEVICECAP_SendPartialObject))); 683 capabilities.setCanEditObjects( 684 static_cast<bool>( 685 LIBMTP_Check_Capability( 686 device.m_device, 687 LIBMTP_DEVICECAP_EditObjects))); 688 } 689 #endif 690 691 return capabilities; 692 } 693 694 bool MTPDevice::listDevices(bool verbose, const std::string &dev_file) 695 { 696 int raw_devices_cnt; 697 LIBMTP_raw_device_t *raw_devices; 698 699 // Do not output LIBMTP debug stuff 700 StreamHelper::off(); 701 LIBMTP_error_number_t err = LIBMTP_Detect_Raw_Devices( 702 &raw_devices, &raw_devices_cnt); 703 StreamHelper::on(); 704 705 if (err != 0) { 706 if (err == LIBMTP_ERROR_NO_DEVICE_ATTACHED) 707 std::cerr << "No raw devices found.\n"; 708 return false; 709 } 710 711 uint8_t bnum, dnum; 712 if (!dev_file.empty() && !smtpfs_usb_devpath(dev_file, &bnum, &dnum)) { 713 std::cerr << "Can not open such device '" << dev_file << "'.\n"; 714 return false; 715 } 716 717 for (int i = 0; i < raw_devices_cnt; ++i) { 718 if (!dev_file.empty() && 719 !(bnum == raw_devices[i].bus_location && dnum == raw_devices[i].devnum)) 720 continue; 721 std::cout << i + 1 << ": " 722 << (raw_devices[i].device_entry.vendor ? raw_devices[i].device_entry.vendor : "Unknown vendor ") 723 << (raw_devices[i].device_entry.product ? raw_devices[i].device_entry.product : "Unknown product") 724 << std::endl; 725 #ifdef HAVE_LIBMTP_CHECK_CAPABILITY 726 MTPDevice dev; 727 if (verbose) { 728 if (!dev.connect(&raw_devices[i])) 729 return false; 730 731 const MTPDevice::Capabilities &cap = dev.getCapabilities(); 732 std::cout << " - can get partial object: " << (cap.canGetPartialObject() ? "yes" : "no") << std::endl; 733 std::cout << " - can send partial object: " << (cap.canSendPartialObject() ? "yes" : "no") << std::endl; 734 std::cout << " - can edit objects : " << (cap.canEditObjects() ? "yes" : "no") << std::endl; 735 dev.disconnect(); 736 } 737 #endif 738 } 739 free(static_cast<void*>(raw_devices)); 740 741 return true; 742 } 743