pcloudcc-lneely/pclsync/pfolder.c

1077 lines
33 KiB
C

/*
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 <pthread.h>
#include <stddef.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#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;
}