//returns first file entry in linked list. if not in cache will be downloaded. int caching_list_directory(const char* path, dir_entry** list) { debugf(DBG_LEVEL_EXT, "caching_list_directory(%s)", path); pthread_mutex_lock(&dcachemut); bool new_entry = false; if (!strcmp(path, "/")) path = ""; dir_cache* cw; for (cw = dcache; cw; cw = cw->next) { if (cw->was_deleted == true) { debugf(DBG_LEVEL_EXT, KMAG"caching_list_directory status: dir(%s) is empty as cached expired, reload from cloud", cw->path); if (!cloudfs_list_directory(cw->path, list)) debugf(DBG_LEVEL_EXT, KMAG"caching_list_directory status: cannot reload dir(%s)", cw->path); else { debugf(DBG_LEVEL_EXT, KMAG"caching_list_directory status: reloaded dir(%s)", cw->path); //cw->entries = *list; cw->was_deleted = false; cw->cached = time(NULL); } } if (cw->was_deleted == false) { if (!strcmp(cw->path, path)) break; } } if (!cw) { //trying to download this entry from cloud, list will point to cached or downloaded entries if (!cloudfs_list_directory(path, list)) { //download was not ok pthread_mutex_unlock(&dcachemut); debugf(DBG_LEVEL_EXT, "exit 0: caching_list_directory(%s) "KYEL"[CACHE-DIR-MISS]", path); return 0; } debugf(DBG_LEVEL_EXT, "caching_list_directory: new_cache(%s) "KYEL"[CACHE-CREATE]", path); cw = new_cache(path); new_entry = true; } else if (cache_timeout > 0 && (time(NULL) - cw->cached > cache_timeout)) { if (!cloudfs_list_directory(path, list)) { //mutex unlock was forgotten pthread_mutex_unlock(&dcachemut); debugf(DBG_LEVEL_EXT, "exit 1: caching_list_directory(%s)", path); return 0; } //fixme: this frees dir subentries but leaves the dir parent entry, this confuses path_info //which believes this dir has no entries if (cw->entries != NULL) { cloudfs_free_dir_list(cw->entries); cw->was_deleted = true; cw->cached = time(NULL); debugf(DBG_LEVEL_EXT, "caching_list_directory(%s) "KYEL"[CACHE-EXPIRED]", path); } else { debugf(DBG_LEVEL_EXT, "got NULL on caching_list_directory(%s) "KYEL"[CACHE-EXPIRED w NULL]", path); pthread_mutex_unlock(&dcachemut); return 0; } } else { debugf(DBG_LEVEL_EXT, "caching_list_directory(%s) "KGRN"[CACHE-DIR-HIT]", path); *list = cw->entries; } //adding new dir file list to global cache, now this dir becomes visible in cache cw->entries = *list; pthread_mutex_unlock(&dcachemut); debugf(DBG_LEVEL_EXT, "exit 2: caching_list_directory(%s)", path); return 1; }
int cloudfs_list_directory(const char *path, dir_entry **dir_list) { char container[MAX_PATH_SIZE * 3] = ""; char object[MAX_PATH_SIZE] = ""; char last_subdir[MAX_PATH_SIZE] = ""; int prefix_length = 0; int response = 0; int retval = 0; int entry_count = 0; *dir_list = NULL; xmlNode *onode = NULL, *anode = NULL, *text_node = NULL; xmlParserCtxtPtr xmlctx = xmlCreatePushParserCtxt(NULL, NULL, "", 0, NULL); if (!strcmp(path, "") || !strcmp(path, "/")) { path = ""; strncpy(container, "/?format=xml", sizeof(container)); } else { sscanf(path, "/%[^/]/%[^\n]", container, object); char *encoded_container = curl_escape(container, 0); char *encoded_object = curl_escape(object, 0); // The empty path doesn't get a trailing slash, everything else does char *trailing_slash; prefix_length = strlen(object); if (object[0] == 0) trailing_slash = ""; else { trailing_slash = "/"; prefix_length++; } snprintf(container, sizeof(container), "%s?format=xml&delimiter=/&prefix=%s%s", encoded_container, encoded_object, trailing_slash); curl_free(encoded_container); curl_free(encoded_object); } response = send_request("GET", container, NULL, xmlctx, NULL); xmlParseChunk(xmlctx, "", 0, 1); if (xmlctx->wellFormed && response >= 200 && response < 300) { xmlNode *root_element = xmlDocGetRootElement(xmlctx->myDoc); for (onode = root_element->children; onode; onode = onode->next) { if (onode->type != XML_ELEMENT_NODE) continue; char is_object = !strcasecmp((const char *)onode->name, "object"); char is_container = !strcasecmp((const char *)onode->name, "container"); char is_subdir = !strcasecmp((const char *)onode->name, "subdir"); if (is_object || is_container || is_subdir) { entry_count++; dir_entry *de = (dir_entry *)malloc(sizeof(dir_entry)); de->next = NULL; de->size = 0; de->last_modified = time(NULL); if (is_container || is_subdir) de->content_type = strdup("application/directory"); for (anode = onode->children; anode; anode = anode->next) { char *content = "<?!?>"; for (text_node = anode->children; text_node; text_node = text_node->next) if (text_node->type == XML_TEXT_NODE) content = (char *)text_node->content; if (!strcasecmp((const char *)anode->name, "name")) { de->name = strdup(content + prefix_length); // Remove trailing slash char *slash = strrchr(de->name, '/'); if (slash && (0 == *(slash + 1))) *slash = 0; if (asprintf(&(de->full_name), "%s/%s", path, de->name) < 0) de->full_name = NULL; } if (!strcasecmp((const char *)anode->name, "bytes")) de->size = strtoll(content, NULL, 10); if (!strcasecmp((const char *)anode->name, "content_type")) { de->content_type = strdup(content); char *semicolon = strchr(de->content_type, ';'); if (semicolon) *semicolon = '\0'; } if (!strcasecmp((const char *)anode->name, "last_modified")) { struct tm last_modified; strptime(content, "%FT%T", &last_modified); de->last_modified = mktime(&last_modified); } } de->isdir = de->content_type && ((strstr(de->content_type, "application/folder") != NULL) || (strstr(de->content_type, "application/directory") != NULL)); if (de->isdir) { if (!strncasecmp(de->name, last_subdir, sizeof(last_subdir))) { cloudfs_free_dir_list(de); continue; } strncpy(last_subdir, de->name, sizeof(last_subdir)); } de->next = *dir_list; *dir_list = de; } else { debugf("unknown element: %s", onode->name); } } retval = 1; } debugf("entry count: %d", entry_count); xmlFreeDoc(xmlctx->myDoc); xmlFreeParserCtxt(xmlctx); return retval; }