diff --git a/camlibs/ptp2/library.c b/camlibs/ptp2/library.c index 21977b883..fb6b0c391 100644 --- a/camlibs/ptp2/library.c +++ b/camlibs/ptp2/library.c @@ -7969,34 +7969,24 @@ find_child (PTPParams *params, const char *path, uint32_t storage, uint32_t hand const char *slash = strchr (path, '/'); size_t filename_len = slash ? (size_t)(slash - path) : strlen(path); - ret = ptp_list_folder (params, storage, handle); + PTPObjectHandles handles = {0}; + ret = ptp_list_folder (params, storage, handle, &handles); if (ret != PTP_RC_OK) return PTP_HANDLER_SPECIAL; - for_each (PTPObject*, ob, params->objects) { - uint32_t oid = ob->oid; - - ret = PTP_RC_OK; - if ((ob->flags & (PTPOBJECT_PARENTOBJECT_LOADED|PTPOBJECT_STORAGEID_LOADED)) != (PTPOBJECT_PARENTOBJECT_LOADED|PTPOBJECT_STORAGEID_LOADED)) - ret = ptp_object_want (params, oid, PTPOBJECT_PARENTOBJECT_LOADED|PTPOBJECT_STORAGEID_LOADED, &ob); - if (ret != PTP_RC_OK) { - /* NOTE: the "i" array entry might now be invalid, as object_want can remove objects from the list */ - GP_LOG_D("failed getting info of oid 0x%08x?", oid); - /* could happen if file gets removed between */ + for_each (uint32_t*, poid, handles) { + PTPObject *ob; + if (PTP_RC_OK != ptp_object_want (params, *poid, PTPOBJECT_PARENTOBJECT_LOADED, &ob)) // refresh + continue; /* object might have been deleted but ObjectRemoved event not handled, yet */ + if (ob->oi.ParentObject != handle) continue; - } - if ((ob->oi.StorageID==storage) && (ob->oi.ParentObject==handle)) { - ret = ptp_object_want (params, oid, PTPOBJECT_OBJECTINFO_LOADED, &ob); - if (ret != PTP_RC_OK) { - GP_LOG_D("failed getting info of oid 0x%08x?", oid); - /* could happen if file gets removed between */ - /* FIXME: should remove, but then we irritate our list */ - continue; - } - if (!strncmp (ob->oi.Filename, path, filename_len)) { - if (retob) *retob = ob; - return oid; - } + + if (PTP_RC_OK != ptp_object_want (params, *poid, PTPOBJECT_OBJECTINFO_LOADED, &ob)) // refresh + continue; /* object might have been deleted but ObjectRemoved event not handled, yet */ + if (!strncmp (ob->oi.Filename, path, filename_len)) { + if (retob) + *retob = ob; + return *poid; } } /* else not found */ @@ -8008,11 +7998,8 @@ folder_to_handle(PTPParams *params, const char *folder, uint32_t storage, uint32 { GP_LOG_D("(folder='%s', storage=0x%08x, parent=0x%08x)", folder, storage, parent); if (retob) *retob = NULL; - if (!strlen(folder) || !strcmp(folder, "/")) { - /* was initially read, no need to reread */ - /* ptp_list_folder (params, storage, 0); */ + if (!strlen(folder) || !strcmp(folder, "/")) return PTP_HANDLER_ROOT; - } if (folder[0] == '/') folder++; @@ -8050,113 +8037,99 @@ find_storage_and_handle_from_path(PTPParams *params, const char *folder, uint32_ } static int -file_list_func (CameraFilesystem *fs, const char *folder, CameraList *list, - void *data, GPContext *context) +generic_list_func (PTPParams *params, const char *folder, int is_directory, CameraList *list) { - Camera *camera = (Camera *)data; - PTPParams *params = &camera->pl->params; - uint32_t parent, storage=0x0000000; - unsigned int i, hasgetstorageids; - SET_CONTEXT_P(params, context); - unsigned int lastobjects_len = params->objects.len, redoneonce = 0; - - GP_LOG_D ("file_list_func(%s)", folder); - - /* There should be NO files in root folder */ - if (!strcmp(folder, "/")) - return (GP_OK); + /* Look for objects that are either files or directories. + * Currently we specify *any* PTP association as directory. + */ - if (!strcmp(folder, "/special")) { - for_each (special_file*, psf, special_files) - CR (gp_list_append (list, psf->name, NULL)); - return (GP_OK); - } + uint32_t storage, parent; CR (find_storage_and_handle_from_path(params, folder, &storage, &parent)); - C_PTP_REP (ptp_list_folder (params, storage, parent)); - GP_LOG_D ("after list folder"); - - hasgetstorageids = ptp_operation_issupported(params,PTP_OC_GetStorageIDs); + /* list this directory */ + PTPObjectHandles handles = {0}; + C_PTP (ptp_list_folder (params, storage, parent, &handles)); + GP_LOG_D ("ptp_list_folder(storage=0x%08x, handle=0x%08x) found %d object handles", storage, parent, handles.len); -retry: - for (i = 0; i < params->objects.len; i++) { + for_each (uint32_t*, phandle, handles) { PTPObject *ob; - uint16_t ret; - uint32_t handle; - - /* not our parent -> next */ - C_PTP_REP (ptp_object_want (params, params->objects.val[i].oid, PTPOBJECT_PARENTOBJECT_LOADED|PTPOBJECT_STORAGEID_LOADED, &ob)); - - /* DANGER DANGER: i is now invalid as objects might have been inserted in the list! */ - - if (ob->oi.ParentObject!=parent) - continue; + if (PTP_RC_OK != ptp_object_want (params, *phandle, PTPOBJECT_PARENTOBJECT_LOADED, &ob)) { // refresh + GP_LOG_D ("could not find object 0x%08x, possibly deleted on camera", *phandle); + continue; /* object might have been deleted but ObjectRemoved event not handled, yet */ + } - /* not on our storage devices -> next */ - if ((hasgetstorageids && (ob->oi.StorageID != storage))) + /* If a filtered ptp_getobjecthandles in ptp_list_folder is not supported by the device, + * we might end up having handles for all objects in 'handles', therefore we check the parent */ + if ((ob->oi.ParentObject != parent)) continue; - handle = ob->oid; /* ob might change or even become invalid in the function below */ - ret = ptp_object_want (params, handle, PTPOBJECT_OBJECTINFO_LOADED, &ob); - if (ret != PTP_RC_OK) { - /* we might raced another delete or ongoing addition, seen on a D810 */ - if (ret == PTP_RC_InvalidObjectHandle) { - GP_LOG_D ("Handle %08x was in list, but not/no longer found via getobjectinfo.\n", handle); - /* remove it for now, we will readd it later if we see it again. */ - ptp_remove_object_from_cache(params, handle); - continue; + if (!(ob->flags & PTPOBJECT_OBJECTINFO_LOADED)) + if (PTP_RC_OK != ptp_object_want (params, *phandle, PTPOBJECT_OBJECTINFO_LOADED, &ob)) { // refresh + GP_LOG_D ("could not find object 0x%08x, possibly deleted on camera", *phandle); + continue; /* object might have been deleted but ObjectRemoved event not handled, yet */ } - C_PTP_REP (ret); - } - /* Is a directory -> next */ - if (ob->oi.ObjectFormat == PTP_OFC_Association) - continue; - log_objectinfo(params, &ob->oi); + /* only looking for directories or files */ + if (is_directory != (ob->oi.ObjectFormat == PTP_OFC_Association)) + continue; if (!ob->oi.Filename) continue; - if (1) { + /* GP_LOG_D ("adding 0x%08x to folder", ob->oid); */ + +#if 0 /* TODO: Axel disabled this for its performance panalty and questionable value. */ /* HP Photosmart 850, the camera tends to duplicate filename in the list. * Original patch by clement.rezvoy@gmail.com */ /* search backwards, likely gets hits faster. */ /* FIXME Marcus: This is also O(n^2) ... bad for large directories. */ if (GP_OK == gp_list_find_by_name(list, NULL, ob->oi.Filename)) { GP_LOG_E ( - "Duplicate filename '%s' in folder '%s'. Ignoring nth entry.\n", + "Duplicate entry '%s' in folder '%s'. Ignoring nth entry.\n", ob->oi.Filename, folder); continue; } - } +#endif CR(gp_list_append (list, ob->oi.Filename, NULL)); } - /* Did we change the object tree list during our traversal? if yes, redo the scan. */ - if (params->objects.len != lastobjects_len) { - if (redoneonce++) { - GP_LOG_E("list changed again on second pass, returning anyway"); - return GP_OK; - } - lastobjects_len = params->objects.len; - gp_list_reset(list); - goto retry; - } + GP_LOG_D ("returning list with %d %s entries", gp_list_count(list), is_directory ? "directory" : "file"); + return GP_OK; } +static int +file_list_func (CameraFilesystem *fs, const char *folder, CameraList *list, + void *data, GPContext *context) +{ + PTPParams *params = &((Camera *)data)->pl->params; + + SET_CONTEXT_P(params, context); + GP_LOG_D ("file_list_func(%s)", folder); + + /* There should be NO files in root folder */ + if (!strcmp(folder, "/")) + return (GP_OK); + + if (!strcmp(folder, "/special")) { + for_each (special_file*, psf, special_files) + CR (gp_list_append (list, psf->name, NULL)); + return (GP_OK); + } + + return generic_list_func(params, folder, FALSE, list); +} + static int folder_list_func (CameraFilesystem *fs, const char *folder, CameraList *list, void *data, GPContext *context) { PTPParams *params = &((Camera *)data)->pl->params; - unsigned int i, hasgetstorageids; - uint32_t handler,storage; - unsigned int redoneonce = 0, lastobjects_len = params->objects.len; SET_CONTEXT_P(params, context); GP_LOG_D ("folder_list_func(%s)", folder); + /* add storage pseudofolders in root folder */ if (!strcmp(folder, "/")) { /* use the cached storageids. they should be valid after camera_init */ @@ -8191,63 +8164,7 @@ folder_list_func (CameraFilesystem *fs, const char *folder, CameraList *list, return (GP_OK); } - CR (find_storage_and_handle_from_path(params, folder, &storage, &handler)); - - /* list this directory */ - C_PTP_REP (ptp_list_folder (params, storage, handler)); - - GP_LOG_D ("after list folder (storage=0x%08x, handler=0x%08x)", storage, handler); - - /* Look for objects we can present as directories. - * Currently we specify *any* PTP association as directory. - */ - hasgetstorageids = ptp_operation_issupported(params,PTP_OC_GetStorageIDs); -retry: - for (i = 0; i < params->objects.len; i++) { - PTPObject *ob; - uint16_t ret; - uint32_t handle; - - C_PTP_REP (ptp_object_want (params, params->objects.val[i].oid, PTPOBJECT_STORAGEID_LOADED|PTPOBJECT_PARENTOBJECT_LOADED, &ob)); - - /* DANGER DANGER: i is now invalid as objects might have been inserted in the list! */ - - if (ob->oi.ParentObject != handler) - continue; - if (hasgetstorageids && (ob->oi.StorageID != storage)) - continue; - - handle = ob->oid; - ret = ptp_object_want (params, handle, PTPOBJECT_OBJECTINFO_LOADED, &ob); - if (ret != PTP_RC_OK) { - /* we might raced another delete or ongoing addition, seen on a D810 */ - if (ret == PTP_RC_InvalidObjectHandle) { - GP_LOG_D ("Handle %08x was in list, but not/no longer found via getobjectinfo.\n", handle); - /* remove it for now, we will readd it later if we see it again. */ - ptp_remove_object_from_cache(params, handle); - continue; - } - C_PTP_REP (ret); - } - if (ob->oi.ObjectFormat!=PTP_OFC_Association) - continue; - GP_LOG_D ("adding 0x%x / ob=%p to folder", ob->oid, ob); - if (GP_OK == gp_list_find_by_name(list, NULL, ob->oi.Filename)) { - GP_LOG_E ( "Duplicated foldername '%s' in folder '%s'. should not happen!\n", ob->oi.Filename, folder); - continue; - } - CR (gp_list_append (list, ob->oi.Filename, NULL)); - } - if (lastobjects_len != params->objects.len) { - if (redoneonce++) { - GP_LOG_E("list changed again on second pass, returning anyway"); - return GP_OK; - } - lastobjects_len = params->objects.len; - gp_list_reset (list); - goto retry; - } - return GP_OK; + return generic_list_func(params, folder, TRUE, list); } /* To avoid roundtrips for querying prop desc @@ -9992,16 +9909,6 @@ camera_init (Camera *camera, GPContext *context) gp_port_set_timeout (camera->port, timeout); } - /* avoid doing this on the Sonys DSLRs in control mode, they hang. :( */ - - if (params->deviceinfo.VendorExtensionID != PTP_VENDOR_SONY) - ptp_list_folder (params, PTP_HANDLER_SPECIAL, PTP_HANDLER_SPECIAL); - - for_each (uint32_t*, psid, params->storageids) { - if ((*psid & 0xffff) && (*psid != 0x80000001)) - ptp_list_folder (params, *psid, PTP_HANDLER_SPECIAL); - } - /* moved down here in case the filesystem needs to first be initialized as the Olympus app does */ if (params->deviceinfo.VendorExtensionID == PTP_VENDOR_GP_OLYMPUS_OMD) { @@ -10012,12 +9919,6 @@ camera_init (Camera *camera, GPContext *context) free_array (¶ms->storageids); C_PTP (ptp_getstorageids(params, ¶ms->storageids)); - /* refetch root */ - for_each (uint32_t*, psid, params->storageids) { - if ((*psid & 0xffff) && (*psid != 0x80000001)) - ptp_list_folder (params, *psid, PTP_HANDLER_SPECIAL); - } - /* if(params->storageids.len > 0) { // Olympus app gets storage info for first item, so emulating here PTPStorageInfo storageinfo; diff --git a/camlibs/ptp2/ptp.c b/camlibs/ptp2/ptp.c index ade4e14f9..8d6ca79d4 100644 --- a/camlibs/ptp2/ptp.c +++ b/camlibs/ptp2/ptp.c @@ -3210,7 +3210,7 @@ ptp_add_event (PTPParams *params, PTPContainer *event) /* CANON EOS fast directory mode: uses ptp_canon_eos_getobjectinfoex to get list of * ObjectInfos instead of just a list of handles that have to be queried the one by one.*/ static uint16_t -ptp_list_folder_eos (PTPParams *params, uint32_t storage, uint32_t handle) { +ptp_list_folder_eos (PTPParams *params, uint32_t storage, uint32_t handle, PTPObjectHandles *children) { unsigned int i, last, changed; PTPCANONFolderEntry *tmp = NULL; unsigned int nroftmp = 0; @@ -3230,7 +3230,7 @@ ptp_list_folder_eos (PTPParams *params, uint32_t storage, uint32_t handle) { for_each (uint32_t*, psid, storageids) { if ((*psid & 0xffff) == 0) { - ptp_debug (params, "list_folder_eos(storage=0x%08x, handle=0x%08x) skipped (invalid storate)", *psid, handle); + ptp_debug (params, "list_folder_eos(storage=0x%08x, handle=0x%08x) skipped (invalid storage)", *psid, handle); continue; } ptp_debug (params, "list_folder_eos(storage=0x%08x, handle=0x%08x)", *psid, handle); @@ -3242,11 +3242,18 @@ ptp_list_folder_eos (PTPParams *params, uint32_t storage, uint32_t handle) { free_array (&storageids); return ret; } + + if (children) + array_extend_capacity(children, nroftmp); + /* convert read entries into objectinfos */ for (i=0;ival[children->len++] = tmp[i].ObjectHandle; + /* TODO: the following is actually slow when adding new entries, as the complete list needs to be traversed. * Make better use of the fact that the list is sorted. */ for (j=0; jobjects.len; j++) { @@ -3309,18 +3316,18 @@ ptp_list_folder_eos (PTPParams *params, uint32_t storage, uint32_t handle) { } uint16_t -ptp_list_folder (PTPParams *params, uint32_t storage, uint32_t handle) { +ptp_list_folder (PTPParams *params, uint32_t storage, uint32_t handle, PTPObjectHandles *children) { unsigned int changed, last; uint16_t ret; uint32_t xhandle = handle; PTPObjectHandles handles = {0}; ptp_debug (params, "ptp_list_folder(storage=0x%08x, handle=0x%08x)", storage, handle); - /* handle=0 is only not read when there is no object in the list yet - * and we do the initial read. */ - if (!handle && params->objects.len) - return PTP_RC_OK; - /* but we can override this to read 0 object of storages */ + + if (children) + array_init(children); + + /* TODO: remove special handling of root folder by introducing a 'root' object */ if (handle == PTP_HANDLER_SPECIAL) handle = 0; @@ -3332,8 +3339,14 @@ ptp_list_folder (PTPParams *params, uint32_t storage, uint32_t handle) { return ret; if (ob->oi.ObjectFormat != PTP_OFC_Association) return PTP_RC_GeneralError; - if (ob->flags & PTPOBJECT_DIRECTORY_LOADED) + if (ob->flags & PTPOBJECT_DIRECTORY_LOADED) { + if (children) { + for_each (PTPObject*, pob, params->objects) + if (pob->oi.ParentObject == handle) + array_push_back(children, pob->oid); + } return PTP_RC_OK; + } ob->flags |= PTPOBJECT_DIRECTORY_LOADED; /*log_objectinfo(params, handle, &ob->oi);*/ } @@ -3341,7 +3354,7 @@ ptp_list_folder (PTPParams *params, uint32_t storage, uint32_t handle) { /* Canon EOS Fast directory strategy */ if ((params->deviceinfo.VendorExtensionID == PTP_VENDOR_CANON) && ptp_operation_issupported(params, PTP_OC_CANON_EOS_GetObjectInfoEx)) { - ret = ptp_list_folder_eos (params, storage, handle); + ret = ptp_list_folder_eos (params, storage, handle, children); if (ret == PTP_RC_OK) return ret; } @@ -3408,7 +3421,6 @@ ptp_list_folder (PTPParams *params, uint32_t storage, uint32_t handle) { fallback: #endif - ptp_debug (params, "Listing ... "); if (handle == 0) xhandle = PTP_HANDLER_SPECIAL; /* 0 would mean all */ ret = ptp_getobjecthandles (params, storage, 0, xhandle, &handles); @@ -3437,7 +3449,7 @@ ptp_list_folder (PTPParams *params, uint32_t storage, uint32_t handle) { } } if (ob == NULL) { - ptp_debug (params, "adding new object: handle 0x%08x (nrofobs=%d,j=%d)", *phandle, params->objects.len,j); + ptp_debug (params, "adding new object: handle 0x%08x (nrofobs=%d,j=%d)", *phandle, params->objects.len, j); array_push_back_empty (¶ms->objects, &ob); @@ -3446,7 +3458,7 @@ ptp_list_folder (PTPParams *params, uint32_t storage, uint32_t handle) { ob->flags = 0; /* root directory list files might return all files, so avoid tagging it */ if (handle != PTP_HANDLER_SPECIAL && handle) { - ptp_debug (params, " parenthandle 0x%08x", handle); + ptp_debug (params, " parent 0x%08x", handle); if (*phandle == handle) { /* EOS bug where handle == parent(handle) */ ob->oi.ParentObject = 0; } else { @@ -3461,7 +3473,7 @@ ptp_list_folder (PTPParams *params, uint32_t storage, uint32_t handle) { } changed = 1; } else { - ptp_debug (params, "adding old object: handle 0x%08x (nrofobs=%d,j=%d)", *phandle, params->objects.len,j); + ptp_debug (params, "adding old object: handle 0x%08x (nrofobs=%d,j=%d)", *phandle, params->objects.len, j); /* for speeding up search */ last = (last+j) % params->objects.len; if (handle != PTP_HANDLER_SPECIAL) { @@ -3474,9 +3486,12 @@ ptp_list_folder (PTPParams *params, uint32_t storage, uint32_t handle) { } } } - free_array (&handles); if (changed) ptp_objects_sort (params); + if (children) + *children = handles; + else + free_array (&handles); return PTP_RC_OK; } @@ -3506,15 +3521,6 @@ handle_event_internal (PTPParams *params, PTPContainer *event) free_array_recusive (¶ms->objects, ptp_free_object); params->storagechanged = 1; - /* mirror what we do in camera_init, fetch root directory entries. */ - if (params->deviceinfo.VendorExtensionID != PTP_VENDOR_SONY) - ptp_list_folder (params, PTP_HANDLER_SPECIAL, PTP_HANDLER_SPECIAL); - - for_each (uint32_t*, psid, params->storageids) { - if ((*psid & 0xffff) && (*psid != 0x80000001)) - ptp_list_folder (params, *psid, PTP_HANDLER_SPECIAL); - } - break; } default: /* check if we should handle it internally too */ diff --git a/camlibs/ptp2/ptp.h b/camlibs/ptp2/ptp.h index f7fdb8fbc..72af01387 100644 --- a/camlibs/ptp2/ptp.h +++ b/camlibs/ptp2/ptp.h @@ -4913,7 +4913,7 @@ uint16_t ptp_object_want (PTPParams *, uint32_t handle, unsigned int want, PTPOb void ptp_objects_sort (PTPParams *); uint16_t ptp_find_object_in_cache (PTPParams *params, uint32_t handle, PTPObject **retob); uint16_t ptp_find_or_insert_object_in_cache (PTPParams *params, uint32_t handle, PTPObject **retob); -uint16_t ptp_list_folder (PTPParams *params, uint32_t storage, uint32_t handle); +uint16_t ptp_list_folder (PTPParams *params, uint32_t storage, uint32_t handle, PTPObjectHandles *children); PTPDevicePropDesc* ptp_find_dpd_in_cache(PTPParams *params, uint32_t dpc);