/* Copyright (c) 2013-2014 Anton Titov. Copyright (c) 2013-2014 pCloud Ltd. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of pCloud Ltd nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL pCloud Ltd BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include "papi.h" #include "pcryptofolder.h" #include "pdiff.h" #include "pfoldersync.h" #include "pfsfolder.h" #include "plibs.h" #include "plist.h" #include "pmem.h" #include "pnetlibs.h" #include "ppathstatus.h" #include "prun.h" #include "psys.h" #include "ppath.h" #include "pfile.h" #include "psql.h" #define INITIAL_NAME_BUFF 2000 #define INITIAL_ENTRY_CNT 128 typedef struct _string_list { psync_list list; char *str; size_t len; } string_list; static inline int psync_crypto_is_error(const void *ptr) { return (uintptr_t)ptr <= PSYNC_CRYPTO_MAX_ERROR; } static inline int psync_crypto_to_error(const void *ptr) { return -((int)(uintptr_t)ptr); } psync_folderid_t pfolder_id(const char *path) { psync_folderid_t cfolderid; const char *sl; psync_sql_res *res; psync_uint_row row; size_t len; res = NULL; if (*path != '/') goto err; cfolderid = 0; while (1) { while (*path == '/') path++; if (*path == 0) { if (res) psql_free(res); return cfolderid; } sl = strchr(path, '/'); if (sl) len = sl - path; else len = strlen(path); if (!res) { res = psql_query_rdlock( "SELECT id FROM folder WHERE parentfolderid=? AND name=?"); if (pdbg_unlikely(!res)) { psync_error = PERROR_DATABASE_ERROR; return PSYNC_INVALID_FOLDERID; } } else psql_reset(res); psql_bind_uint(res, 1, cfolderid); psql_bind_lstr(res, 2, path, len); row = psql_fetch_int(res); if (pdbg_unlikely(!row)) goto err; cfolderid = row[0]; path += len; } err: if (res) psql_free(res); psync_error = PERROR_REMOTE_FOLDER_NOT_FOUND; return PSYNC_INVALID_FOLDERID; } static psync_folderid_t wait_folder_id_in_db(psync_folderid_t folderid) { psync_sql_res *res; psync_uint_row row; int tries; tries = 0; while (++tries <= 20) { res = psql_query_rdlock("SELECT id FROM folder WHERE id=?"); psql_bind_uint(res, 1, folderid); row = psql_fetch_int(res); psql_free(res); if (row) return folderid; psys_sleep_milliseconds(50); } return PSYNC_INVALID_FOLDERID; } psync_folderid_t pfolder_db_wait(psync_folderid_t folderid) { return wait_folder_id_in_db(folderid); } psync_folderid_t pfolder_id_create(const char *path) { psync_folderid_t cfolderid; const char *sl; psync_sql_res *res; psync_uint_row row; size_t len; res = NULL; if (*path != '/') goto err; cfolderid = 0; while (1) { while (*path == '/') path++; if (*path == 0) { if (res) psql_free(res); return wait_folder_id_in_db(cfolderid); } sl = strchr(path, '/'); if (sl) len = sl - path; else len = strlen(path); if (!res) { res = psql_query_rdlock( "SELECT id FROM folder WHERE parentfolderid=? AND name=?"); if (pdbg_unlikely(!res)) { psync_error = PERROR_DATABASE_ERROR; return PSYNC_INVALID_FOLDERID; } } else psql_reset(res); psql_bind_uint(res, 1, cfolderid); psql_bind_lstr(res, 2, path, len); row = psql_fetch_int(res); if (row) cfolderid = row[0]; else { binparam params[] = {PAPI_STR("auth", psync_my_auth), PAPI_NUM("folderid", cfolderid), PAPI_LSTR("name", path, len)}; psock_t *api; binresult *bres; uint64_t result; api = psync_apipool_get(); if (unlikely(!api)) goto errnet; bres = papi_send2(api, "createfolderifnotexists", params); if (bres) psync_apipool_release(api); else psync_apipool_release_bad(api); if (unlikely(!bres)) goto errnet; result = papi_find_result2(bres, "result", PARAM_NUM)->num; if (result == 0) { cfolderid = papi_find_result2(papi_find_result2(bres, "metadata", PARAM_HASH), "folderid", PARAM_NUM) ->num; if (papi_find_result2(bres, "created", PARAM_BOOL)->num) pdiff_wake(); pmem_free(PMEM_SUBSYS_OTHER, bres); } else { pmem_free(PMEM_SUBSYS_OTHER, bres); psync_process_api_error(result); if (psync_handle_api_result(result) == PSYNC_NET_TEMPFAIL) goto errnet; else goto err; } } path += len; } err: if (res) psql_free(res); psync_error = PERROR_REMOTE_FOLDER_NOT_FOUND; return PSYNC_INVALID_FOLDERID; errnet: if (res) psql_free(res); psync_error = PERROR_OFFLINE; return PSYNC_INVALID_FOLDERID; } static void free_string_list_element(string_list *elem) { pmem_free(PMEM_SUBSYS_OTHER, elem); } static void psync_free_string_list(psync_list *lst) { psync_list_for_each_element_call(lst, string_list, list, free_string_list_element); } static string_list *str_to_list_element(const char *str, size_t len) { string_list *le; le = (string_list *)pmem_malloc(PMEM_SUBSYS_OTHER, sizeof(string_list) + len + 1); le->str = (char *)(le + 1); le->len = len; memcpy(le->str, str, len + 1); return le; } static int psync_add_path_to_list(psync_list *lst, psync_folderid_t folderid) { string_list *e; psync_sql_res *res; psync_variant_row row; const char *str; size_t len; while (1) { if (folderid == 0) { e = (string_list *)pmem_malloc(PMEM_SUBSYS_OTHER, sizeof(string_list)); e->str = (char *)e; e->len = 0; psync_list_add_head(lst, &e->list); return 0; } res = psql_query_rdlock( "SELECT parentfolderid, name FROM folder WHERE id=?"); psql_bind_uint(res, 1, folderid); row = psql_fetch(res); if (unlikely(!row)) break; folderid = psync_get_number(row[0]); str = psync_get_lstring(row[1], &len); e = str_to_list_element(str, len); psync_list_add_head(lst, &e->list); psql_free(res); } psql_free(res); pdbg_logf(D_ERROR, "folder %lu not found in database", (unsigned long)folderid); return -1; } static string_list *str_list_decode(psync_folderid_t folderid, string_list *e) { pcrypto_textdec_t dec; char *fn; dec = pcryptofolder_flddecoder_get(folderid); if (psync_crypto_is_error(dec)) { pmem_free(PMEM_SUBSYS_OTHER, e); pdbg_logf(D_WARNING, "got error %d getting decoder for folderid %lu", psync_crypto_to_error(dec), (unsigned long)folderid); return NULL; } fn = pcryptofolder_flddecode_filename(dec, e->str); pcryptofolder_flddecoder_release(folderid, dec); pmem_free(PMEM_SUBSYS_OTHER, e); if (pdbg_unlikely(!fn)) return NULL; e = str_to_list_element(fn, strlen(fn)); pmem_free(PMEM_SUBSYS_OTHER, fn); return e; } static int psync_add_path_to_list_decode(psync_list *lst, psync_folderid_t folderid) { string_list *e, *c; psync_sql_res *res; psync_variant_row row; const char *str; psync_folderid_t cfolderid; size_t len; uint32_t flags; e = NULL; while (1) { if (folderid == 0) { if (e) psync_list_add_head(lst, &e->list); e = (string_list *)pmem_malloc(PMEM_SUBSYS_OTHER, sizeof(string_list)); e->str = (char *)e; e->len = 0; psync_list_add_head(lst, &e->list); return 0; } res = psql_query_rdlock( "SELECT parentfolderid, name, flags FROM folder WHERE id=?"); psql_bind_uint(res, 1, folderid); row = psql_fetch(res); if (pdbg_unlikely(!row)) break; cfolderid = folderid; flags = psync_get_number(row[2]); folderid = psync_get_number(row[0]); str = psync_get_lstring(row[1], &len); c = str_to_list_element(str, len); psql_free(res); if (e) { if (flags & PSYNC_FOLDER_FLAG_ENCRYPTED) { e = str_list_decode(cfolderid, e); if (pdbg_unlikely(!e)) goto err; } psync_list_add_head(lst, &e->list); } e = c; } psql_free(res); err: pdbg_logf(D_ERROR, "folder %lu not found in database", (unsigned long)folderid); return -1; } char *psync_join_string_list(const char *sep, psync_list *lst, size_t *retlen) { size_t slen, seplen, cnt, total_size; string_list *e; char *ret, *str; slen = cnt = 0; psync_list_for_each_element(e, lst, string_list, list) { slen += e->len; cnt++; } if (unlikely(!cnt)) return putil_strdup(""); seplen = strlen(sep); if (cnt != 0 && seplen > SIZE_MAX / cnt) { return NULL; } total_size = slen + cnt * seplen + 1; ret = str = pmem_malloc(PMEM_SUBSYS_OTHER, total_size); psync_list_for_each_element(e, lst, string_list, list) { memcpy(str, e->str, e->len); str += e->len; memcpy(str, sep, seplen); str += seplen; } str -= seplen; *str = 0; if (retlen) *retlen = str - ret; return ret; } char *pfolder_path(psync_folderid_t folderid, size_t *retlen) { psync_list folderlist; char *ret; int res; psync_list_init(&folderlist); psql_rdlock(); res = psync_add_path_to_list(&folderlist, folderid); psql_rdunlock(); if (pdbg_unlikely(res)) { psync_free_string_list(&folderlist); return PSYNC_INVALID_PATH; } ret = psync_join_string_list("/", &folderlist, retlen); psync_free_string_list(&folderlist); if (!ret[0]) { pmem_free(PMEM_SUBSYS_OTHER, ret); ret = putil_strdup("/"); if (retlen) *retlen = 1; } return ret; } char *pfolder_path_sep(psync_folderid_t folderid, const char *sep, size_t *retlen) { psync_list folderlist; char *ret; int res; psync_list_init(&folderlist); res = psync_add_path_to_list_decode(&folderlist, folderid); if (pdbg_unlikely(res)) { psync_free_string_list(&folderlist); return PSYNC_INVALID_PATH; } ret = psync_join_string_list(sep, &folderlist, retlen); psync_free_string_list(&folderlist); if (!ret[0]) { pmem_free(PMEM_SUBSYS_OTHER, ret); ret = putil_strdup(sep); if (retlen) *retlen = 1; } return ret; } char *pfolder_file_path(psync_fileid_t fileid, size_t *retlen) { psync_list folderlist; char *ret; psync_sql_res *res; psync_variant_row row; string_list *e; const char *str; psync_folderid_t folderid; size_t len; psync_list_init(&folderlist); psql_rdlock(); res = psql_query_rdlock( "SELECT parentfolderid, name FROM file WHERE id=?"); psql_bind_uint(res, 1, fileid); row = psql_fetch(res); if (pdbg_unlikely(!row)) { psql_free(res); psql_rdunlock(); return PSYNC_INVALID_PATH; } folderid = psync_get_number(row[0]); str = psync_get_lstring(row[1], &len); e = str_to_list_element(str, len); psync_list_add_head(&folderlist, &e->list); psql_free(res); if (pdbg_unlikely(psync_add_path_to_list(&folderlist, folderid))) { psql_rdunlock(); psync_free_string_list(&folderlist); return PSYNC_INVALID_PATH; } psql_rdunlock(); ret = psync_join_string_list("/", &folderlist, retlen); psync_free_string_list(&folderlist); return ret; } static int psync_add_local_path_to_list_by_localfolderid( psync_list *lst, psync_folderid_t localfolderid, psync_syncid_t syncid) { string_list *e, *le; psync_sql_res *res; psync_variant_row row; const char *str; size_t len; res = psql_query_rdlock("SELECT localpath FROM syncfolder WHERE id=?"); psql_bind_uint(res, 1, syncid); row = psql_fetch(res); if (unlikely(!row)) { pdbg_logf(D_ERROR, "could not find sync id %lu", (long unsigned)syncid); psql_free(res); return -1; } str = psync_get_lstring(row[0], &len); le = str_to_list_element(str, len); psql_free(res); while (1) { if (localfolderid == 0) { psync_list_add_head(lst, &le->list); return 0; } res = psql_query_rdlock( "SELECT localparentfolderid, name FROM localfolder WHERE id=?"); psql_bind_uint(res, 1, localfolderid); row = psql_fetch(res); if (unlikely(!row)) break; localfolderid = psync_get_number(row[0]); str = psync_get_lstring(row[1], &len); e = str_to_list_element(str, len); psync_list_add_head(lst, &e->list); psql_free(res); } psql_free(res); psync_list_add_head(lst, &le->list); pdbg_logf(D_ERROR, "local folder %lu not found in database", (unsigned long)localfolderid); return -1; } char *pfolder_lpath_lfldr(psync_folderid_t localfolderid, psync_syncid_t syncid, size_t *retlen) { psync_list folderlist; char *ret; int res; psync_list_init(&folderlist); psql_rdlock(); res = psync_add_local_path_to_list_by_localfolderid(&folderlist, localfolderid, syncid); psql_rdunlock(); if (pdbg_unlikely(res)) { psync_free_string_list(&folderlist); return PSYNC_INVALID_PATH; } ret = psync_join_string_list("/", &folderlist, retlen); psync_free_string_list(&folderlist); return ret; } char *pfolder_lpath_lfile(psync_fileid_t localfileid, size_t *retlen) { psync_list folderlist; char *ret; const char *str; psync_sql_res *res; psync_variant_row row; string_list *e; psync_folderid_t localfolderid; size_t len; psync_syncid_t syncid; int rs; psync_list_init(&folderlist); psql_rdlock(); res = psql_query_nolock( "SELECT localparentfolderid, syncid, name FROM localfile WHERE id=?"); psql_bind_uint(res, 1, localfileid); if (pdbg_unlikely(!(row = psql_fetch(res)))) { psql_free(res); psql_rdunlock(); psync_free_string_list(&folderlist); return PSYNC_INVALID_PATH; } localfolderid = psync_get_number(row[0]); syncid = psync_get_number(row[1]); str = psync_get_lstring(row[2], &len); e = str_to_list_element(str, len); psql_free(res); psync_list_add_head(&folderlist, &e->list); rs = psync_add_local_path_to_list_by_localfolderid(&folderlist, localfolderid, syncid); psql_rdunlock(); if (pdbg_unlikely(rs)) { psync_free_string_list(&folderlist); return PSYNC_INVALID_PATH; } ret = psync_join_string_list("/", &folderlist, retlen); psync_free_string_list(&folderlist); return ret; } static folder_list *folder_list_init() { folder_list *list; list = (folder_list *)pmem_malloc(PMEM_SUBSYS_OTHER, sizeof(folder_list)); list->entries = (pentry_t *)pmem_malloc_array(PMEM_SUBSYS_OTHER, INITIAL_ENTRY_CNT, sizeof(pentry_t)); list->namebuff = (char *)pmem_malloc(PMEM_SUBSYS_OTHER, INITIAL_NAME_BUFF); list->nameoff = 0; list->namealloc = INITIAL_NAME_BUFF; list->entriescnt = 0; list->entriesalloc = INITIAL_ENTRY_CNT; return list; } static void folder_list_add(folder_list *list, pentry_t *entry) { if (list->entriescnt >= list->entriesalloc) { list->entriesalloc *= 2; list->entries = (pentry_t *)pmem_realloc(PMEM_SUBSYS_OTHER, list->entries, sizeof(pentry_t) * list->entriesalloc); } while (list->nameoff + entry->namelen >= list->namealloc) { list->namealloc *= 2; list->namebuff = (char *)pmem_realloc(PMEM_SUBSYS_OTHER, list->namebuff, list->namealloc); } memcpy(&list->entries[list->entriescnt++], entry, sizeof(pentry_t)); memcpy(list->namebuff + list->nameoff, entry->name, entry->namelen); list->nameoff += entry->namelen; list->namebuff[list->nameoff++] = 0; } static void folder_list_free(folder_list *list) { pmem_free(PMEM_SUBSYS_OTHER, list->entries); pmem_free(PMEM_SUBSYS_OTHER, list->namebuff); pmem_free(PMEM_SUBSYS_OTHER, list); } static pfolder_list_t *folder_list_finalize(folder_list *list) { pfolder_list_t *ret; char *name; uint32_t i; pdbg_logf(D_NOTICE, "allocating %u bytes for folder list, %u of which for names", (unsigned)(offsetof(pfolder_list_t, entries) + sizeof(pentry_t) * list->entriescnt + list->nameoff), (unsigned)list->nameoff); ret = (pfolder_list_t *)pmem_malloc(PMEM_SUBSYS_OTHER, offsetof(pfolder_list_t, entries) + sizeof(pentry_t) * list->entriescnt + list->nameoff); name = ((char *)ret) + offsetof(pfolder_list_t, entries) + sizeof(pentry_t) * list->entriescnt; ret->entrycnt = list->entriescnt; memcpy(ret->entries, list->entries, sizeof(pentry_t) * list->entriescnt); memcpy(name, list->namebuff, list->nameoff); for (i = 0; i < list->entriescnt; i++) { ret->entries[i].name = name; name += list->entries[i].namelen + 1; } folder_list_free(list); return ret; } pfolder_list_t *pfolder_remote_folders(psync_folderid_t folderid, psync_listtype_t listtype) { folder_list *list; psync_sql_res *res; psync_variant_row row; size_t namelen; pentry_t entry; uint64_t perms, flags; list = folder_list_init(); char *tmp; int parentencrypted = 0; if (listtype & PLIST_FOLDERS) { res = psql_query_rdlock("SELECT flags FROM folder WHERE id=?"); psql_bind_uint(res, 1, folderid); if ((row = psql_fetch(res))) { parentencrypted = (psync_get_number(row[0]) & PSYNC_FOLDER_FLAG_ENCRYPTED) ? 1 : 0; } else { pdbg_logf(D_ERROR, "Can't find folder with id %lu", folderid); psql_free(res); return NULL; } psql_free(res); res = psql_query_rdlock( "SELECT id, permissions, name, userid, flags FROM folder WHERE " "parentfolderid=? ORDER BY name"); psql_bind_uint(res, 1, folderid); while ((row = psql_fetch(res))) { entry.folder.folderid = psync_get_number(row[0]); perms = psync_get_number(row[1]); flags = psync_get_number(row[4]); entry.folder.cansyncup = ((perms & PSYNC_PERM_WRITE) == PSYNC_PERM_WRITE) && ((flags & (PSYNC_FOLDER_FLAG_BACKUP_DEVICE_LIST | PSYNC_FOLDER_FLAG_BACKUP_DEVICE | PSYNC_FOLDER_FLAG_BACKUP_ROOT | PSYNC_FOLDER_FLAG_BACKUP)) == 0); entry.folder.cansyncdown = ((perms & PSYNC_PERM_READ) == PSYNC_PERM_READ) && ((flags & (PSYNC_FOLDER_FLAG_BACKUP_DEVICE_LIST | PSYNC_FOLDER_FLAG_BACKUP_DEVICE | PSYNC_FOLDER_FLAG_BACKUP_ROOT | PSYNC_FOLDER_FLAG_BACKUP)) == 0); entry.folder.canshare = (psync_my_userid == psync_get_number(row[3])); entry.folder.isencrypted = (psync_get_number(row[4]) & PSYNC_FOLDER_FLAG_ENCRYPTED) ? 1 : 0; if (parentencrypted && pcryptofolder_is_unlocked()) { tmp = (char *)psync_get_lstring(row[2], &namelen); entry.name = pfs_fldr_get_decname(folderid, tmp, namelen); if (!entry.name) { pdbg_logf( D_BUG, "Can't decrypt folder name for folderid: %lu, parent folfderid: " "%lu, cryptoerr: %d, encrypted name: %s. Skippping ...", entry.folder.folderid, folderid, pfs_fldr_crypto_error(), tmp); continue; } entry.namelen = strlen(entry.name); } else { entry.name = psync_get_lstring(row[2], &namelen); entry.namelen = namelen; } entry.isfolder = 1; folder_list_add(list, &entry); } psql_free(res); } if (listtype & PLIST_FILES) { res = psql_query_rdlock( "SELECT id, size, name FROM file WHERE parentfolderid=? ORDER BY name"); psql_bind_uint(res, 1, folderid); while ((row = psql_fetch(res))) { entry.file.fileid = psync_get_number(row[0]); entry.file.size = psync_get_number(row[1]); entry.name = psync_get_lstring(row[2], &namelen); entry.namelen = namelen; entry.isfolder = 0; folder_list_add(list, &entry); } psql_free(res); } return folder_list_finalize(list); } static void add_to_folderlist(void *ptr, ppath_stat *stat) { flist_ltype *ft = (flist_ltype *)ptr; pentry_t entry; int isfolder = pfile_stat_isfolder(&stat->stat); if (((ft->listtype & PLIST_FOLDERS) && isfolder) || ((ft->listtype & PLIST_FILES) && !isfolder)) { entry.name = stat->name; entry.namelen = strlen(stat->name); if (isfolder) { entry.isfolder = 1; entry.folder.folderid = pfile_stat_inode(&stat->stat); entry.folder.cansyncup = pfile_stat_mode_ok(&stat->stat, 5); entry.folder.cansyncdown = pfile_stat_mode_ok(&stat->stat, 7); entry.folder.isencrypted = 0; } else { entry.isfolder = 0; entry.file.fileid = pfile_stat_inode(&stat->stat); entry.file.size = pfile_stat_size(&stat->stat); } folder_list_add(ft->folderlist, &entry); } } pfolder_list_t *pfolder_local_folders(const char *path, psync_listtype_t listtype) { flist_ltype ft; ft.folderlist = folder_list_init(); ft.listtype = listtype; if (ppath_ls(path, add_to_folderlist, &ft)) { folder_list_free(ft.folderlist); return NULL; } else return folder_list_finalize(ft.folderlist); } pentry_t *pfolder_stat(const char *remotepath) { psync_folderid_t folderid; psync_sql_res *res; psync_uint_row row; pentry_t *ret; char *cremotepath; size_t len, olen; if (remotepath[0] != '/') return NULL; if (remotepath[1] == 0) { ret = pmem_malloc(PMEM_SUBSYS_OTHER, sizeof(pentry_t)); ret->name = "/"; ret->namelen = 1; ret->isfolder = 1; ret->folder.folderid = 0; ret->folder.cansyncup = 1; ret->folder.cansyncdown = 1; ret->folder.canshare = 0; } olen = len = strlen(remotepath); while (remotepath[--len] != '/') ; if (len == 0) folderid = 0; else { cremotepath = pmem_malloc_array(PMEM_SUBSYS_OTHER, len + 1, sizeof(char)); memcpy(cremotepath, remotepath, len + 1); cremotepath[len] = 0; folderid = pfolder_id(cremotepath); pmem_free(PMEM_SUBSYS_OTHER, cremotepath); if (folderid == PSYNC_INVALID_FOLDERID) return NULL; } len++; olen -= len; res = psql_query_rdlock("SELECT id, permissions, userid, flags FROM " "folder WHERE parentfolderid=? AND name=?"); psql_bind_uint(res, 1, folderid); psql_bind_lstr(res, 2, remotepath + len, olen); if ((row = psql_fetch_int(res))) { ret = (pentry_t *)pmem_malloc(PMEM_SUBSYS_OTHER, sizeof(pentry_t) + olen + 1); ret->folder.folderid = row[0]; ret->folder.cansyncup = ((row[1] & PSYNC_PERM_WRITE) == PSYNC_PERM_WRITE); ret->folder.cansyncdown = ((row[1] & PSYNC_PERM_READ) == PSYNC_PERM_READ); ret->folder.canshare = (psync_my_userid == row[2]); ret->folder.isencrypted = (row[3] & PSYNC_FOLDER_FLAG_ENCRYPTED) ? 1 : 0; ret->name = (char *)(ret + 1); ret->namelen = olen; ret->isfolder = 1; memcpy(ret + 1, remotepath + len, olen + 1); psql_free(res); return ret; } psql_free(res); res = psql_query_rdlock( "SELECT id, size FROM file WHERE parentfolderid=? AND name=?"); psql_bind_uint(res, 1, folderid); psql_bind_lstr(res, 2, remotepath + len, olen); if ((row = psql_fetch_int(res))) { ret = (pentry_t *)pmem_malloc(PMEM_SUBSYS_OTHER, sizeof(pentry_t) + olen + 1); ret->file.fileid = row[0]; ret->file.size = row[1]; ret->name = (char *)(ret + 1); ret->namelen = olen; ret->isfolder = 0; memcpy(ret + 1, remotepath + len, olen + 1); psql_free(res); return ret; } psql_free(res); return NULL; } typedef struct { char *localpath; char *remotepath; size_t locallen; size_t remotelen; psync_folderid_t folderid; psync_syncid_t syncid; psync_synctype_t synctype; } psync_tmp_folder_t; psync_folder_list_t *pfolder_sync_folders(char *syncTypes) { psync_sql_res *res; psync_variant_row row; psync_tmp_folder_t *folders; const char *cstr; char *str; psync_folder_list_t *ret; size_t strlens, l; psync_folderid_t folderid; uint32_t alloced, lastfolder, i; char sql[1024]; folders = NULL; alloced = lastfolder = 0; strlens = 0; if (strlen(syncTypes) > 0) { putil_slprintf(sql, 1024, "SELECT id, folderid, localpath, synctype FROM syncfolder " "WHERE folderid IS NOT NULL AND synctype IN (%s)", syncTypes); } else { putil_slprintf(sql, 1024, "SELECT id, folderid, localpath, synctype FROM syncfolder " "WHERE folderid IS NOT NULL"); } res = psql_query_rdlock(sql); while ((row = psql_fetch(res))) { if (alloced == lastfolder) { alloced = (alloced + 32) * 2; folders = (psync_tmp_folder_t *)pmem_realloc(PMEM_SUBSYS_OTHER, folders, sizeof(psync_tmp_folder_t) * alloced); } cstr = psync_get_lstring(row[2], &l); l++; str = (char *)pmem_malloc(PMEM_SUBSYS_OTHER, l); memcpy(str, cstr, l); strlens += l; folders[lastfolder].localpath = str; folders[lastfolder].locallen = l; folderid = psync_get_number(row[1]); str = pfolder_path(folderid, &l); if (unlikely(!str)) { str = putil_strdup("/Invalid/Path"); l = strlen(str); } l++; strlens += l; folders[lastfolder].remotepath = str; folders[lastfolder].remotelen = l; folders[lastfolder].folderid = folderid; folders[lastfolder].syncid = psync_get_number(row[0]); folders[lastfolder].synctype = psync_get_number(row[3]); lastfolder++; } psql_free(res); l = offsetof(psync_folder_list_t, folders) + sizeof(psync_folder_t) * lastfolder; ret = (psync_folder_list_t *)pmem_malloc(PMEM_SUBSYS_OTHER, l + strlens); str = ((char *)ret) + l; ret->foldercnt = lastfolder; for (i = 0; i < lastfolder; i++) { l = folders[i].locallen; memcpy(str, folders[i].localpath, l); pmem_free(PMEM_SUBSYS_OTHER, folders[i].localpath); strncpy(ret->folders[i].localpath, str, sizeof(ret->folders[i].localpath) - 1); ret->folders[i].localpath[sizeof(ret->folders[i].localpath) - 1] = '\0'; l--; while (l && str[l] != '/' && str[l] != '/') l--; if ((str[l] == '/' || str[l] == '/') && str[l + 1]) l++; strncpy(ret->folders[i].localname, str, sizeof(ret->folders[i].localname) - 1); ret->folders[i].localname[sizeof(ret->folders[i].localname) - 1] = '\0'; str += folders[i].locallen; l = folders[i].remotelen; memcpy(str, folders[i].remotepath, l); pmem_free(PMEM_SUBSYS_OTHER, folders[i].remotepath); strncpy(ret->folders[i].remotepath, str, sizeof(ret->folders[i].remotepath) - 1); ret->folders[i].remotepath[sizeof(ret->folders[i].remotepath) - 1] = '\0'; if (l) l--; while (l && str[l] != '/') l--; if (str[l] == '/') l++; strncpy(ret->folders[i].remotename, str + l, sizeof(ret->folders[i].remotename) - 1); ret->folders[i].remotename[sizeof(ret->folders[i].remotename) - 1] = '\0'; str += folders[i].remotelen; ret->folders[i].folderid = folders[i].folderid; ret->folders[i].syncid = folders[i].syncid; ret->folders[i].synctype = folders[i].synctype; } pmem_free(PMEM_SUBSYS_OTHER, folders); return ret; } psync_syncid_t pfolder_add_sync(const char *localpath, psync_folderid_t folderid, psync_synctype_t synctype) { psync_sql_res *res; char *syncmp; psync_uint_row row; psync_str_row srow; uint64_t perms; struct stat st; psync_syncid_t ret; int unsigned mbedtls_md; pdbg_logf(D_NOTICE, "Add sync by folder id localpath: [%s]", localpath); if (pdbg_unlikely(synctype < PSYNC_SYNCTYPE_MIN || synctype > PSYNC_SYNCTYPE_MAX)) return_isyncid(PERROR_INVALID_SYNCTYPE); if (pdbg_unlikely(stat(localpath, &st)) || pdbg_unlikely(!pfile_stat_isfolder(&st))) return_isyncid(PERROR_LOCAL_FOLDER_NOT_FOUND); if (synctype & PSYNC_DOWNLOAD_ONLY) mbedtls_md = 7; else mbedtls_md = 5; if (pdbg_unlikely(!pfile_stat_mode_ok(&st, mbedtls_md))) return_isyncid(PERROR_LOCAL_FOLDER_ACC_DENIED); syncmp = pfs_getmountpoint(); if (syncmp) { size_t len = strlen(syncmp); if (!memcmp(syncmp, localpath, len) && (localpath[len] == 0 || localpath[len] == '/' || localpath[len] == '\\')) { pmem_free(PMEM_SUBSYS_OTHER, syncmp); return_isyncid(PERROR_LOCAL_IS_ON_PDRIVE); } pmem_free(PMEM_SUBSYS_OTHER, syncmp); } res = psql_query("SELECT localpath FROM syncfolder"); if (pdbg_unlikely(!res)) return_isyncid(PERROR_DATABASE_ERROR); while ((srow = psql_fetch_str(res))) if (psyncer_str_has_prefix(srow[0], localpath)) { psql_free(res); return_isyncid(PERROR_PARENT_OR_SUBFOLDER_ALREADY_SYNCING); } else if (!strcmp(srow[0], localpath)) { psql_free(res); return_isyncid(PERROR_FOLDER_ALREADY_SYNCING); } psql_free(res); if (folderid) { res = psql_query("SELECT permissions FROM folder WHERE id=?"); if (pdbg_unlikely(!res)) return_isyncid(PERROR_DATABASE_ERROR); psql_bind_uint(res, 1, folderid); row = psql_fetch_int(res); if (pdbg_unlikely(!row)) { psql_free(res); return_isyncid(PERROR_REMOTE_FOLDER_NOT_FOUND); } perms = row[0]; psql_free(res); } else perms = PSYNC_PERM_ALL; if (pdbg_unlikely((synctype & PSYNC_DOWNLOAD_ONLY && (perms & PSYNC_PERM_READ) != PSYNC_PERM_READ) || (synctype & PSYNC_UPLOAD_ONLY && (perms & PSYNC_PERM_WRITE) != PSYNC_PERM_WRITE))) return_isyncid(PERROR_REMOTE_FOLDER_ACC_DENIED); res = psql_prepare( "INSERT OR IGNORE INTO syncfolder (folderid, localpath, synctype, flags, " "inode, deviceid) VALUES (?, ?, ?, 0, ?, ?)"); if (pdbg_unlikely(!res)) return_isyncid(PERROR_DATABASE_ERROR); psql_bind_uint(res, 1, folderid); psql_bind_str(res, 2, localpath); psql_bind_uint(res, 3, synctype); psql_bind_uint(res, 4, pfile_stat_inode(&st)); psql_bind_uint(res, 5, pfile_stat_device(&st)); psql_run(res); if (pdbg_likely(psql_affected())) ret = psql_insertid(); else ret = PSYNC_INVALID_SYNCID; psql_free(res); if (ret == PSYNC_INVALID_SYNCID) return_isyncid(PERROR_FOLDER_ALREADY_SYNCING); psql_sync(); ppathstatus_reload_syncs(); psyncer_create(ret); return ret; } psync_syncid_t pfolder_add_sync_path(const char *localpath, const char *remotepath, psync_synctype_t synctype) { psync_folderid_t folderid = pfolder_id(remotepath); if (pdbg_likely(folderid != PSYNC_INVALID_FOLDERID)) return pfolder_add_sync(localpath, folderid, synctype); else return PSYNC_INVALID_SYNCID; } int pfolder_add_sync_path_delay(const char *localpath, const char *remotepath, psync_synctype_t synctype) { psync_sql_res *res; struct stat st; int unsigned mbedtls_md; if (pdbg_unlikely(synctype < PSYNC_SYNCTYPE_MIN || synctype > PSYNC_SYNCTYPE_MAX)) { psync_error = PERROR_INVALID_SYNCTYPE; return -1; } if (pdbg_unlikely(stat(localpath, &st)) || pdbg_unlikely(!pfile_stat_isfolder(&st))) { psync_error = PERROR_LOCAL_FOLDER_NOT_FOUND; return -1; } if (synctype & PSYNC_DOWNLOAD_ONLY) mbedtls_md = 7; else mbedtls_md = 5; if (pdbg_unlikely(!pfile_stat_mode_ok(&st, mbedtls_md))) { psync_error = PERROR_LOCAL_FOLDER_ACC_DENIED; return -1; } res = psql_prepare("INSERT INTO syncfolderdelayed (localpath, " "remotepath, synctype) VALUES (?, ?, ?)"); psql_bind_str(res, 1, localpath); psql_bind_str(res, 2, remotepath); psql_bind_uint(res, 3, synctype); psql_run_free(res); psql_sync(); if (pstatus_get(PSTATUS_TYPE_ONLINE) == PSTATUS_ONLINE_ONLINE) prun_thread("check delayed syncs", psyncer_check_delayed); return 0; } pfolder_list_t *pfolder_remote_folders_path(const char *remotepath, psync_listtype_t listtype) { psync_folderid_t folderid = pfolder_id(remotepath); if (folderid != PSYNC_INVALID_FOLDERID) return pfolder_remote_folders(folderid, listtype); else return NULL; }