1938 lines
68 KiB
C
1938 lines
68 KiB
C
/*
|
|
Copyright (c) 2014 Anton Titov.
|
|
|
|
Copyright (c) 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 "plibs.h"
|
|
#include "pstatus.h"
|
|
#include "ptimer.h"
|
|
#include "psettings.h"
|
|
#include "pfsupload.h"
|
|
#include "plist.h"
|
|
#include "pnetlibs.h"
|
|
#include "pfstasks.h"
|
|
#include "pfileops.h"
|
|
#include "ppagecache.h"
|
|
#include "pssl.h"
|
|
#include "pfsxattr.h"
|
|
#include "pupload.h"
|
|
#include "pfscrypto.h"
|
|
#include "pcache.h"
|
|
#include "ppathstatus.h"
|
|
#include "pdiff.h"
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
|
|
#include "pcallbacks.h"
|
|
|
|
typedef struct {
|
|
psync_list list;
|
|
binresult *res;
|
|
uint64_t id;
|
|
uint64_t type;
|
|
psync_folderid_t folderid;
|
|
psync_folderid_t sfolderid;
|
|
psync_fileid_t fileid;
|
|
const char *text1;
|
|
const char *text2;
|
|
int64_t int1;
|
|
int64_t int2;
|
|
unsigned char ccreat;
|
|
unsigned char needprocessing;
|
|
unsigned char status;
|
|
} fsupload_task_t;
|
|
|
|
static pthread_mutex_t upload_mutex=PTHREAD_MUTEX_INITIALIZER;
|
|
static pthread_cond_t upload_cond=PTHREAD_COND_INITIALIZER;
|
|
static uint64_t current_upload_taskid=0;
|
|
static uint32_t upload_wakes=0;
|
|
static int large_upload_running=0;
|
|
static int stop_current_upload=0;
|
|
static psync_list *current_upload_batch=NULL;
|
|
|
|
static const uint32_t requiredstatuses[]={
|
|
PSTATUS_COMBINE(PSTATUS_TYPE_AUTH, PSTATUS_AUTH_PROVIDED),
|
|
PSTATUS_COMBINE(PSTATUS_TYPE_RUN, PSTATUS_RUN_RUN),
|
|
PSTATUS_COMBINE(PSTATUS_TYPE_ONLINE, PSTATUS_ONLINE_ONLINE),
|
|
PSTATUS_COMBINE(PSTATUS_TYPE_ACCFULL, PSTATUS_ACCFULL_QUOTAOK)
|
|
};
|
|
|
|
static const uint32_t requiredstatusesnooverquota[]={
|
|
PSTATUS_COMBINE(PSTATUS_TYPE_AUTH, PSTATUS_AUTH_PROVIDED),
|
|
PSTATUS_COMBINE(PSTATUS_TYPE_RUN, PSTATUS_RUN_RUN),
|
|
PSTATUS_COMBINE(PSTATUS_TYPE_ONLINE, PSTATUS_ONLINE_ONLINE)
|
|
};
|
|
|
|
static int psync_send_task_mkdir(psync_socket *api, fsupload_task_t *task){
|
|
if (task->text2){
|
|
binparam params[]={P_STR("auth", psync_my_auth), P_NUM("folderid", task->folderid), P_STR("name", task->text1), P_STR("timeformat", "timestamp"),
|
|
P_BOOL("encrypted", 1), P_STR("key", task->text2), P_NUM("ctime", task->int1)};
|
|
if (likely_log(send_command_no_res(api, "createfolderifnotexists", params)==PTR_OK))
|
|
return 0;
|
|
else
|
|
return -1;
|
|
}
|
|
else{
|
|
binparam params[]={P_STR("auth", psync_my_auth), P_NUM("folderid", task->folderid), P_STR("name", task->text1), P_STR("timeformat", "timestamp"),
|
|
P_NUM("ctime", task->int1)};
|
|
if (likely_log(send_command_no_res(api, "createfolderifnotexists", params)==PTR_OK))
|
|
return 0;
|
|
else
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
static void handle_mkdir_api_error(uint64_t result, fsupload_task_t *task){
|
|
psync_sql_res *res;
|
|
debug(D_ERROR, "createfolderifnotexists returned error %u", (unsigned)result);
|
|
psync_process_api_error(result);
|
|
switch (result){
|
|
case 2002: /* parent does not exists */
|
|
case 2003: /* access denied */
|
|
case 2075: /* not a member of a business account */
|
|
case 2344: /* can't create folders in backup folder */
|
|
res=psync_sql_prep_statement("UPDATE fstask SET folderid=0 WHERE id=?");
|
|
psync_sql_bind_uint(res, 1, task->id);
|
|
psync_sql_run_free(res);
|
|
break;
|
|
case 2001: /* invalid name */
|
|
res=psync_sql_prep_statement("UPDATE fstask SET text1=\"Invalid Name Requested\" WHERE id=?");
|
|
psync_sql_bind_uint(res, 1, task->id);
|
|
psync_sql_run_free(res);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static int psync_process_task_mkdir(fsupload_task_t *task){
|
|
const binresult *meta;
|
|
uint64_t result;
|
|
psync_folderid_t folderid;
|
|
result=psync_find_result(task->res, "result", PARAM_NUM)->num;
|
|
if (result){
|
|
handle_mkdir_api_error(result, task);
|
|
return -1;
|
|
}
|
|
meta=psync_find_result(task->res, "metadata", PARAM_HASH);
|
|
folderid=psync_find_result(meta, "folderid", PARAM_NUM)->num;
|
|
task->int2=folderid;
|
|
psync_ops_create_folder_in_db(meta);
|
|
psync_fstask_folder_created(task->folderid, task->id, folderid, task->text1);
|
|
psync_fs_task_to_folder(task->id, folderid);
|
|
if (task->text2 && psync_find_result(task->res, "created", PARAM_BOOL)->num){
|
|
psync_sql_res *res;
|
|
unsigned char *enckey;
|
|
size_t enckeylen;
|
|
enckey=psync_base64_decode((const unsigned char *)task->text2, strlen(task->text2), &enckeylen);
|
|
if (likely_log(enckey)){
|
|
res=psync_sql_prep_statement("REPLACE INTO cryptofolderkey (folderid, enckey) VALUES (?, ?)");
|
|
psync_sql_bind_uint(res, 1, folderid);
|
|
psync_sql_bind_blob(res, 2, (char *)enckey, enckeylen);
|
|
psync_sql_run_free(res);
|
|
psync_free(enckey);
|
|
}
|
|
}
|
|
debug(D_NOTICE, "folder %lu/%s created", (unsigned long)task->folderid, task->text1);
|
|
return 0;
|
|
}
|
|
|
|
static int psync_send_task_rmdir(psync_socket *api, fsupload_task_t *task){
|
|
binparam params[]={P_STR("auth", psync_my_auth), P_NUM("folderid", task->sfolderid), P_STR("timeformat", "timestamp")};
|
|
if (likely_log(send_command_no_res(api, "deletefolder", params)==PTR_OK))
|
|
return 0;
|
|
else
|
|
return -1;
|
|
}
|
|
|
|
static int handle_rmdir_api_error(uint64_t result, fsupload_task_t *task){
|
|
debug(D_ERROR, "deletefolder returned error %u", (unsigned)result);
|
|
psync_process_api_error(result);
|
|
switch (result){
|
|
case 2005: /* folder does not exist, kind of success */
|
|
//psync_ops_delete_folder_from_db(task->sfolderid);
|
|
psync_fstask_folder_deleted(task->folderid, task->id, task->text1);
|
|
return 0;
|
|
case 2003: /* access denied, skip*/
|
|
case 2006: /* not empty */
|
|
case 2028: /* folder is shared */
|
|
case 2287: /* public folder */
|
|
case 2345: /* backup */
|
|
psync_fstask_folder_deleted(task->folderid, task->id, task->text1);
|
|
return 0;
|
|
default:
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
static int psync_process_task_rmdir(fsupload_task_t *task){
|
|
uint64_t result;
|
|
result=psync_find_result(task->res, "result", PARAM_NUM)->num;
|
|
if (result)
|
|
return handle_rmdir_api_error(result, task);
|
|
psync_ops_delete_folder_from_db(psync_find_result(task->res, "metadata", PARAM_HASH));
|
|
psync_fstask_folder_deleted(task->folderid, task->id, task->text1);
|
|
debug(D_NOTICE, "folder %lu/%s deleted", (unsigned long)task->folderid, task->text1);
|
|
return 0;
|
|
}
|
|
|
|
static int psync_send_task_creat_upload_small(psync_socket *api, fsupload_task_t *task, psync_file_t fd, psync_stat_t *st){
|
|
unsigned char *data;
|
|
uint64_t size;
|
|
size_t len;
|
|
size=psync_stat_size(st);
|
|
if (task->text2){
|
|
#if defined(PSYNC_HAS_BIRTHTIME)
|
|
binparam params[]={P_STR("auth", psync_my_auth), P_NUM("folderid", task->folderid), P_STR("filename", task->text1),
|
|
P_BOOL("nopartial", 1), P_STR("timeformat", "timestamp"), P_NUM("ctime", psync_stat_birthtime(st)),
|
|
P_NUM("mtime", psync_stat_mtime(st)), P_BOOL("encrypted", 1), P_STR("key", task->text2)};
|
|
#else
|
|
binparam params[]={P_STR("auth", psync_my_auth), P_NUM("folderid", task->folderid), P_STR("filename", task->text1),
|
|
P_BOOL("nopartial", 1), P_STR("timeformat", "timestamp"),
|
|
P_NUM("mtime", psync_stat_mtime(st)), P_BOOL("encrypted", 1), P_STR("key", task->text2)};
|
|
#endif
|
|
data=prepare_command_data_alloc("uploadfile", params, size, size, &len);
|
|
}
|
|
else{
|
|
#if defined(PSYNC_HAS_BIRTHTIME)
|
|
binparam params[]={P_STR("auth", psync_my_auth), P_NUM("folderid", task->folderid), P_STR("filename", task->text1),
|
|
P_BOOL("nopartial", 1), P_STR("timeformat", "timestamp"), P_NUM("ctime", psync_stat_birthtime(st)), P_NUM("mtime", psync_stat_mtime(st))};
|
|
#else
|
|
binparam params[]={P_STR("auth", psync_my_auth), P_NUM("folderid", task->folderid), P_STR("filename", task->text1),
|
|
P_BOOL("nopartial", 1), P_STR("timeformat", "timestamp"), P_NUM("mtime", psync_stat_mtime(st))};
|
|
#endif
|
|
data=prepare_command_data_alloc("uploadfile", params, size, size, &len);
|
|
}
|
|
if (unlikely_log(psync_file_read(fd, data+len, size)!=size) || unlikely_log(psync_fs_get_file_writeid(task->id)!=task->int1)){
|
|
psync_free(data);
|
|
return -1;
|
|
}
|
|
size+=len;
|
|
if (unlikely_log(psync_socket_writeall_upload(api, data, size)!=size)){
|
|
psync_free(data);
|
|
return -1;
|
|
}
|
|
else{
|
|
psync_free(data);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static int large_upload_creat_send_write(psync_socket *api, psync_uploadid_t uploadid, uint64_t offset, uint64_t length){
|
|
binparam params[]={P_STR("auth", psync_my_auth), P_NUM("uploadoffset", offset), P_NUM("uploadid", uploadid)};
|
|
if (unlikely_log(!do_send_command(api, "upload_write", strlen("upload_write"), params, ARRAY_SIZE(params), length, 0)))
|
|
return -1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
static int clean_uploads_for_task(psync_socket *api, psync_uploadid_t taskid){
|
|
psync_sql_res *sql;
|
|
psync_full_result_int *fr;
|
|
binresult *res;
|
|
uint32_t i;
|
|
int ret;
|
|
ret=0;
|
|
sql=psync_sql_query_rdlock("SELECT uploadid FROM fstaskupload WHERE fstaskid=?");
|
|
psync_sql_bind_uint(sql, 1, taskid);
|
|
fr=psync_sql_fetchall_int(sql);
|
|
for (i=0; i<fr->rows; i++){
|
|
binparam params[]={P_STR("auth", psync_my_auth), P_NUM("uploadid", psync_get_result_cell(fr, i, 0))};
|
|
res=send_command(api, "upload_delete", params);
|
|
if (!res){
|
|
ret=-1;
|
|
break;
|
|
}
|
|
else
|
|
psync_free(res);
|
|
}
|
|
sql=psync_sql_prep_statement("DELETE FROM fstaskupload WHERE fstaskid=?");
|
|
psync_sql_bind_uint(sql, 1, taskid);
|
|
psync_sql_run_free(sql);
|
|
return ret;
|
|
}
|
|
|
|
/* releases api ONLY on error */
|
|
static int large_upload_check_checksum(psync_socket *api, uint64_t uploadid, const unsigned char *filehash){
|
|
binparam params[]={P_STR("auth", psync_my_auth), P_NUM("uploadid", uploadid)};
|
|
binresult *res;
|
|
uint64_t result;
|
|
res=send_command(api, "upload_info", params);
|
|
if (unlikely_log(!res)){
|
|
psync_apipool_release_bad(api);
|
|
return -1;
|
|
}
|
|
result=psync_find_result(res, "result", PARAM_NUM)->num;
|
|
if (unlikely(result)){
|
|
debug(D_WARNING, "upload_info returned %lu", (long unsigned)result);
|
|
psync_free(res);
|
|
psync_apipool_release(api);
|
|
psync_process_api_error(result);
|
|
return -1;
|
|
}
|
|
if (memcmp(filehash, psync_find_result(res, PSYNC_CHECKSUM, PARAM_STR)->str, PSYNC_HASH_DIGEST_HEXLEN)){
|
|
debug(D_WARNING, "upload_info returned different checksum");
|
|
psync_free(res);
|
|
psync_apipool_release(api);
|
|
return -1;
|
|
}
|
|
psync_free(res);
|
|
return 0;
|
|
}
|
|
|
|
static int handle_upload_api_error_taskid(uint64_t result, uint64_t taskid){
|
|
psync_sql_res *res;
|
|
psync_process_api_error(result);
|
|
switch (result){
|
|
case 2005: /* folder does not exists */
|
|
case 2003: /* access denied */
|
|
case 2075: /* are not a member of a business account */
|
|
case 2346: /* backup folder */
|
|
res=psync_sql_prep_statement("UPDATE fstask SET folderid=0 WHERE id=?");
|
|
psync_sql_bind_uint(res, 1, taskid);
|
|
psync_sql_run_free(res);
|
|
psync_fsupload_wake();
|
|
return -1;
|
|
case 2001: /* invalid filename */
|
|
res=psync_sql_prep_statement("UPDATE fstask SET text1=\"Invalid Name Requested\" WHERE id=?");
|
|
psync_sql_bind_uint(res, 1, taskid);
|
|
psync_sql_run_free(res);
|
|
psync_fsupload_wake();
|
|
return -1;
|
|
case 2008:{ /* overquota */
|
|
int locked=psync_sql_islocked();
|
|
if (locked)
|
|
psync_sql_commit_transaction();
|
|
assert(!psync_sql_islocked());
|
|
psync_milisleep(PSYNC_SLEEP_ON_DISK_FULL);
|
|
if (locked)
|
|
psync_sql_start_transaction();
|
|
return -1;
|
|
}
|
|
case 2124: /* crypto expired */
|
|
res=psync_sql_prep_statement("UPDATE fstask SET status=1 WHERE id=?");
|
|
psync_sql_bind_uint(res, 1, taskid);
|
|
psync_sql_run_free(res);
|
|
return -1;
|
|
default:
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
static int handle_upload_api_error(uint64_t result, fsupload_task_t *task){
|
|
debug(D_ERROR, "uploadfile returned error %u", (unsigned)result);
|
|
return handle_upload_api_error_taskid(result, task->id);
|
|
}
|
|
|
|
static void set_key_for_fileid(psync_fileid_t fileid, uint64_t hash, const char *key){
|
|
char buff[16];
|
|
psync_sql_res *res;
|
|
unsigned char *enckey;
|
|
size_t enckeylen;
|
|
enckey=psync_base64_decode((const unsigned char *)key, strlen(key), &enckeylen);
|
|
if (likely_log(enckey)){
|
|
res=psync_sql_prep_statement("REPLACE INTO cryptofilekey (fileid, hash, enckey) VALUES (?, ?, ?)");
|
|
psync_sql_bind_uint(res, 1, fileid);
|
|
psync_sql_bind_uint(res, 2, hash);
|
|
psync_sql_bind_blob(res, 3, (char *)enckey, enckeylen);
|
|
psync_sql_run_free(res);
|
|
psync_free(enckey);
|
|
}
|
|
psync_get_string_id(buff, "DKEY", fileid);
|
|
psync_cache_del(buff);
|
|
}
|
|
|
|
static int save_meta(const binresult *meta, psync_folderid_t folderid, const char *name, uint64_t taskid, uint64_t writeid, int newfile,
|
|
uint64_t oldhash, const char *key){
|
|
psync_sql_res *sql;
|
|
psync_uint_row row;
|
|
uint64_t hash, size;
|
|
psync_fileid_t fileid;
|
|
int deleted;
|
|
fileid=psync_find_result(meta, "fileid", PARAM_NUM)->num;
|
|
hash=psync_find_result(meta, "hash", PARAM_NUM)->num;
|
|
size=psync_find_result(meta, "size", PARAM_NUM)->num;
|
|
deleted=0;
|
|
psync_sql_start_transaction();
|
|
if (psync_fs_update_openfile(taskid, writeid, fileid, hash, size, psync_find_result(meta, "created", PARAM_NUM)->num)){
|
|
sql=psync_sql_query_nolock("SELECT status FROM fstask WHERE id=?");
|
|
psync_sql_bind_uint(sql, 1, taskid);
|
|
row=psync_sql_fetch_rowint(sql);
|
|
if (row && row[0]==11){
|
|
psync_sql_free_result(sql);
|
|
deleted=1;
|
|
debug(D_NOTICE, "detected cancel of upload of %s too late, processing normally, delete will come", name);
|
|
}
|
|
else{
|
|
psync_sql_free_result(sql);
|
|
psync_sql_rollback_transaction();
|
|
debug(D_NOTICE, "upload of %s task %lu cancelled due to writeid mismatch", name, (unsigned long)taskid);
|
|
return -1;
|
|
}
|
|
}
|
|
if (newfile){
|
|
psync_ops_create_file_in_db(meta);
|
|
if (!deleted)
|
|
psync_pagecache_creat_to_pagecache(taskid, hash, 0);
|
|
psync_fstask_file_created(folderid, taskid, name, fileid);
|
|
psync_fs_task_to_file(taskid, fileid);
|
|
}
|
|
else{
|
|
psync_ops_update_file_in_db(meta);
|
|
if (!deleted)
|
|
psync_pagecache_modify_to_pagecache(taskid, hash, oldhash);
|
|
psync_fstask_file_modified(folderid, taskid, name, fileid);
|
|
}
|
|
if (key)
|
|
set_key_for_fileid(fileid, hash, key);
|
|
sql=psync_sql_prep_statement("DELETE FROM fstaskdepend WHERE dependfstaskid=?");
|
|
psync_sql_bind_uint(sql, 1, taskid);
|
|
psync_sql_run_free(sql);
|
|
if (psync_sql_affected_rows())
|
|
psync_fsupload_wake();
|
|
sql=psync_sql_prep_statement("DELETE FROM fstaskupload WHERE fstaskid=?");
|
|
psync_sql_bind_uint(sql, 1, taskid);
|
|
psync_sql_run_free(sql);
|
|
sql=psync_sql_prep_statement("UPDATE fstask SET fileid=? WHERE fileid=?");
|
|
psync_sql_bind_uint(sql, 1, fileid);
|
|
psync_sql_bind_int(sql, 2, -(psync_fsfileid_t)taskid);
|
|
psync_sql_run_free(sql);
|
|
if (deleted){
|
|
sql=psync_sql_prep_statement("DELETE FROM fstask WHERE id=? AND int1=?");
|
|
psync_sql_bind_uint(sql, 1, taskid);
|
|
psync_sql_bind_uint(sql, 2, writeid);
|
|
psync_sql_run_free(sql);
|
|
}
|
|
else{
|
|
sql=psync_sql_prep_statement("UPDATE fstask SET status=3 WHERE id=? AND int1=?");
|
|
psync_sql_bind_uint(sql, 1, taskid);
|
|
psync_sql_bind_uint(sql, 2, writeid);
|
|
psync_sql_run_free(sql);
|
|
}
|
|
if (!psync_sql_affected_rows()){
|
|
debug(D_BUG, "upload of %s cancelled due to writeid mismatch, writeid %lu, psync_fs_update_openfile should have catched that", name, (long unsigned)writeid);
|
|
psync_sql_rollback_transaction();
|
|
return -1;
|
|
}
|
|
psync_sql_commit_transaction();
|
|
debug(D_NOTICE, "file %lu/%s uploaded (mtime=%lu, size=%lu)", (unsigned long)folderid, name,
|
|
(unsigned long)psync_find_result(meta, "modified", PARAM_NUM)->num,
|
|
(unsigned long)psync_find_result(meta, "size", PARAM_NUM)->num);
|
|
psync_status_recalc_to_upload_async();
|
|
return 0;
|
|
}
|
|
|
|
static int large_upload_save(psync_socket *api, uint64_t uploadid, psync_folderid_t folderid, const char *name,
|
|
uint64_t taskid, uint64_t writeid, int newfile, uint64_t oldhash, const char *key, const char *filepath){
|
|
binresult *res;
|
|
psync_stat_t st;
|
|
uint64_t result;
|
|
int ret;
|
|
if (psync_stat(filepath, &st)){
|
|
debug(D_WARNING, "could not stat file %s", filepath);
|
|
psync_apipool_release(api);
|
|
return -1;
|
|
}
|
|
if (key){
|
|
#if defined(PSYNC_HAS_BIRTHTIME)
|
|
binparam params[]={P_STR("auth", psync_my_auth), P_NUM("folderid", folderid), P_STR("name", name), P_NUM("uploadid", uploadid),
|
|
P_STR("timeformat", "timestamp"), P_NUM("ctime", psync_stat_birthtime(&st)), P_NUM("mtime", psync_stat_mtime(&st)),
|
|
P_BOOL("encrypted", 1), P_STR("key", key)};
|
|
#else
|
|
binparam params[]={P_STR("auth", psync_my_auth), P_NUM("folderid", folderid), P_STR("name", name), P_NUM("uploadid", uploadid),
|
|
P_STR("timeformat", "timestamp"), P_NUM("mtime", psync_stat_mtime(&st)), P_BOOL("encrypted", 1), P_STR("key", key)};
|
|
#endif
|
|
res=send_command(api, "upload_save", params);
|
|
}
|
|
else{
|
|
#if defined(PSYNC_HAS_BIRTHTIME)
|
|
binparam params[]={P_STR("auth", psync_my_auth), P_NUM("folderid", folderid), P_STR("name", name), P_NUM("uploadid", uploadid),
|
|
P_STR("timeformat", "timestamp"), P_NUM("ctime", psync_stat_birthtime(&st)), P_NUM("mtime", psync_stat_mtime(&st))};
|
|
#else
|
|
binparam params[]={P_STR("auth", psync_my_auth), P_NUM("folderid", folderid), P_STR("name", name), P_NUM("uploadid", uploadid),
|
|
P_STR("timeformat", "timestamp"), P_NUM("mtime", psync_stat_mtime(&st))};
|
|
#endif
|
|
res=send_command(api, "upload_save", params);
|
|
}
|
|
if (unlikely_log(!res)){
|
|
psync_apipool_release_bad(api);
|
|
return -1;
|
|
}
|
|
psync_apipool_release(api);
|
|
result=psync_find_result(res, "result", PARAM_NUM)->num;
|
|
if (unlikely(result)){
|
|
debug(D_WARNING, "upload_save returned %lu", (long unsigned)result);
|
|
psync_free(res);
|
|
handle_upload_api_error_taskid(result, taskid);
|
|
return -1;
|
|
}
|
|
debug(D_NOTICE, "sent mtime=%lu", (unsigned long)psync_stat_mtime(&st));
|
|
ret=save_meta(psync_find_result(res, "metadata", PARAM_HASH), folderid, name, taskid, writeid, newfile, oldhash, key);
|
|
psync_free(res);
|
|
psync_diff_wake();
|
|
return ret;
|
|
}
|
|
|
|
static void perm_fail_upload_task(uint64_t taskid){
|
|
psync_sql_res *sql;
|
|
debug(D_WARNING, "failed task %lu", (unsigned long)taskid);
|
|
psync_sql_start_transaction();
|
|
sql=psync_sql_prep_statement("DELETE FROM fstaskdepend WHERE dependfstaskid=?");
|
|
psync_sql_bind_uint(sql, 1, taskid);
|
|
psync_sql_run_free(sql);
|
|
if (psync_sql_affected_rows())
|
|
psync_fsupload_wake();
|
|
sql=psync_sql_prep_statement("DELETE FROM fstask WHERE fileid=?");
|
|
psync_sql_bind_int(sql, 1, -(psync_fsfileid_t)taskid);
|
|
psync_sql_run_free(sql);
|
|
sql=psync_sql_prep_statement("DELETE FROM fstask WHERE id=?");
|
|
psync_sql_bind_uint(sql, 1, taskid);
|
|
psync_sql_run_free(sql);
|
|
psync_fs_task_deleted(taskid);
|
|
psync_sql_commit_transaction();
|
|
psync_status_recalc_to_upload_async();
|
|
}
|
|
|
|
static int copy_file(psync_socket *api, const psync_stat_t *st, psync_fileid_t fileid, uint64_t hash, psync_folderid_t folderid, const char *name,
|
|
uint64_t taskid, uint64_t writeid){
|
|
#if defined(PSYNC_HAS_BIRTHTIME)
|
|
binparam params[]={P_STR("auth", psync_my_auth), P_NUM("fileid", fileid), P_NUM("hash", hash), P_NUM("tofolderid", folderid),
|
|
P_STR("toname", name), P_STR("timeformat", "timestamp"), P_NUM("ctime", psync_stat_birthtime(st)), P_NUM("mtime", psync_stat_mtime(st))};
|
|
#else
|
|
binparam params[]={P_STR("auth", psync_my_auth), P_NUM("fileid", fileid), P_NUM("hash", hash), P_NUM("tofolderid", folderid),
|
|
P_STR("toname", name), P_STR("timeformat", "timestamp"), P_NUM("mtime", psync_stat_mtime(st))};
|
|
#endif
|
|
binresult *res;
|
|
const binresult *meta;
|
|
uint64_t result;
|
|
int ret;
|
|
res=send_command(api, "copyfile", params);
|
|
if (unlikely(!res))
|
|
return -1;
|
|
result=psync_find_result(res, "result", PARAM_NUM)->num;
|
|
if (unlikely(result)){
|
|
psync_free(res);
|
|
debug(D_WARNING, "command copyfile returned code %u", (unsigned)result);
|
|
psync_process_api_error(result);
|
|
return 0;
|
|
}
|
|
meta=psync_find_result(res, "metadata", PARAM_HASH);
|
|
debug(D_NOTICE, "sent mtime=%lu", (unsigned long)psync_stat_mtime(st));
|
|
ret=save_meta(meta, folderid, name, taskid, writeid, 1, 0, NULL);
|
|
psync_free(res);
|
|
if (ret) // ret*2-1?
|
|
return -1;
|
|
else
|
|
return 1;
|
|
}
|
|
|
|
static int copy_file_if_exists(psync_socket *api, const char *filename, const unsigned char *hashhex, uint64_t fsize, psync_folderid_t folderid, const char *name,
|
|
uint64_t taskid, uint64_t writeid){
|
|
binparam params[]={P_STR("auth", psync_my_auth), P_NUM("size", fsize), P_LSTR(PSYNC_CHECKSUM, hashhex, PSYNC_HASH_DIGEST_HEXLEN)};
|
|
binresult *res;
|
|
const binresult *metas, *meta;
|
|
psync_stat_t st;
|
|
uint64_t result;
|
|
int ret;
|
|
res=send_command(api, "getfilesbychecksum", params);
|
|
if (unlikely(!res))
|
|
return -1;
|
|
result=psync_find_result(res, "result", PARAM_NUM)->num;
|
|
if (unlikely(result)){
|
|
psync_free(res);
|
|
debug(D_WARNING, "command getfilesbychecksum returned code %u", (unsigned)result);
|
|
psync_process_api_error(result);
|
|
return 0;
|
|
}
|
|
metas=psync_find_result(res, "metadata", PARAM_ARRAY);
|
|
if (!metas->length){
|
|
psync_free(res);
|
|
return 0;
|
|
}
|
|
meta=metas->array[0];
|
|
if (psync_stat(filename, &st)){
|
|
psync_free(res);
|
|
return -1;
|
|
}
|
|
ret=copy_file(api, &st, psync_find_result(meta, "fileid", PARAM_NUM)->num, psync_find_result(meta, "hash", PARAM_NUM)->num, folderid, name, taskid, writeid);
|
|
if (ret==1){
|
|
debug(D_NOTICE, "file %lu/%s copied to %lu/%s instead of uploading due to matching checksum",
|
|
(long unsigned)psync_find_result(meta, "parentfolderid", PARAM_NUM)->num, psync_find_result(meta, "name", PARAM_STR)->str,
|
|
(long unsigned)folderid, name);
|
|
psync_diff_wake();
|
|
}
|
|
psync_free(res);
|
|
return ret;
|
|
}
|
|
|
|
static int large_upload_creat(uint64_t taskid, psync_folderid_t folderid, const char *name, const char *filename,
|
|
psync_uploadid_t uploadid, uint64_t writeid, const char *key){
|
|
psync_sql_res *sql;
|
|
psync_socket *api;
|
|
binresult *res;
|
|
void *buff;
|
|
uint64_t usize, fsize, result, asize;
|
|
size_t rd;
|
|
ssize_t rrd;
|
|
psync_file_t fd;
|
|
int ret;
|
|
unsigned char uploadhash[PSYNC_HASH_DIGEST_HEXLEN], filehash[PSYNC_HASH_DIGEST_HEXLEN], fileparthash[PSYNC_HASH_DIGEST_HEXLEN];
|
|
debug(D_NOTICE, "uploading %s as %lu/%s (uploadid=%lu)", filename, (unsigned long)folderid, name, (unsigned long)uploadid);
|
|
asize=0;
|
|
if (uploadid){
|
|
ret=psync_get_upload_checksum(uploadid, uploadhash, &usize);
|
|
if (ret!=PSYNC_NET_OK){
|
|
if (ret==PSYNC_NET_TEMPFAIL)
|
|
return -1;
|
|
else
|
|
uploadid=0;
|
|
}
|
|
}
|
|
if (uploadid)
|
|
ret=psync_get_local_file_checksum_part(filename, filehash, &fsize, fileparthash, usize);
|
|
else
|
|
ret=psync_get_local_file_checksum(filename, filehash, &fsize);
|
|
if (ret){
|
|
perm_fail_upload_task(taskid);
|
|
debug(D_WARNING, "could not open local file %s, skipping task", filename);
|
|
return 0;
|
|
}
|
|
if (uploadid && memcmp(fileparthash, uploadhash, PSYNC_HASH_DIGEST_HEXLEN))
|
|
uploadid=0;
|
|
api=psync_apipool_get();
|
|
if (unlikely(!api))
|
|
return -1;
|
|
if (!key){
|
|
ret=copy_file_if_exists(api, filename, filehash, fsize, folderid, name, taskid, writeid);
|
|
if (ret!=0){
|
|
if (ret==1){
|
|
psync_apipool_release(api);
|
|
return 0;
|
|
}
|
|
else{
|
|
psync_apipool_release_bad(api);
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
if (!uploadid || usize>fsize){
|
|
binparam params[]={P_STR("auth", psync_my_auth), P_NUM("filesize", fsize)};
|
|
usize=0;
|
|
res=send_command(api, "upload_create", params);
|
|
if (!res)
|
|
goto err0;
|
|
result=psync_find_result(res, "result", PARAM_NUM)->num;
|
|
if (unlikely(result)){
|
|
psync_free(res);
|
|
psync_apipool_release(api);
|
|
debug(D_WARNING, "upload_create returned %lu", (unsigned long)result);
|
|
psync_process_api_error(result);
|
|
if (psync_handle_api_result(result)==PSYNC_NET_TEMPFAIL)
|
|
return -1;
|
|
else
|
|
return 0;
|
|
}
|
|
uploadid=psync_find_result(res, "uploadid", PARAM_NUM)->num;
|
|
psync_free(res);
|
|
sql=psync_sql_prep_statement("INSERT INTO fstaskupload (fstaskid, uploadid) VALUES (?, ?)");
|
|
psync_sql_bind_uint(sql, 1, taskid);
|
|
psync_sql_bind_uint(sql, 2, uploadid);
|
|
psync_sql_run_free(sql);
|
|
}
|
|
fd=psync_file_open(filename, P_O_RDONLY, 0);
|
|
if (unlikely_log(fd==INVALID_HANDLE_VALUE))
|
|
goto ret0;
|
|
if (usize){
|
|
debug(D_NOTICE, "resuming from offset %lu", (unsigned long)usize);
|
|
if (unlikely_log(psync_file_seek(fd, usize, P_SEEK_SET)==-1))
|
|
goto ret01;
|
|
}
|
|
if (large_upload_creat_send_write(api, uploadid, usize, fsize-usize))
|
|
goto err1;
|
|
buff=psync_malloc(PSYNC_COPY_BUFFER_SIZE);
|
|
if (usize){
|
|
asize=usize;
|
|
psync_upload_add_bytes_uploaded(asize);
|
|
}
|
|
while (usize<fsize){
|
|
if (unlikely(stop_current_upload)){
|
|
debug(D_NOTICE, "got stop for file %s", name);
|
|
goto err2;
|
|
}
|
|
psync_wait_statuses_array(requiredstatuses, ARRAY_SIZE(requiredstatuses));
|
|
if (fsize-usize>PSYNC_COPY_BUFFER_SIZE)
|
|
rd=PSYNC_COPY_BUFFER_SIZE;
|
|
else
|
|
rd=fsize-usize;
|
|
rrd=psync_file_read(fd, buff, rd);
|
|
if (unlikely_log(rrd<=0))
|
|
goto err2;
|
|
usize+=rrd;
|
|
if (unlikely_log(psync_socket_writeall_upload(api, buff, rrd)!=rrd))
|
|
goto err2;
|
|
asize+=rrd;
|
|
psync_upload_add_bytes_uploaded(rrd);
|
|
}
|
|
psync_free(buff);
|
|
psync_file_close(fd);
|
|
res=get_result(api);
|
|
if (unlikely_log(!res))
|
|
goto err0;
|
|
result=psync_find_result(res, "result", PARAM_NUM)->num;
|
|
psync_free(res);
|
|
if (result){
|
|
debug(D_WARNING, "upload_write returned error %lu", (long unsigned)result);
|
|
psync_process_api_error(result);
|
|
if (result==2068 && clean_uploads_for_task(api, taskid))
|
|
psync_apipool_release_bad(api);
|
|
else
|
|
psync_apipool_release(api);
|
|
psync_process_api_error(result);
|
|
goto errs;
|
|
}
|
|
if (unlikely(stop_current_upload)){
|
|
debug(D_NOTICE, "got stop for file %s", name);
|
|
psync_apipool_release(api);
|
|
goto errs;
|
|
}
|
|
// large_upload_check_checksum releases api on failure
|
|
if (large_upload_check_checksum(api, uploadid, filehash))
|
|
goto errs;
|
|
if (unlikely(stop_current_upload)){
|
|
debug(D_NOTICE, "got stop for file %s", name);
|
|
psync_apipool_release(api);
|
|
goto errs;
|
|
}
|
|
if (psync_fs_get_file_writeid(taskid)!=writeid){
|
|
debug(D_NOTICE, "%s changed while uploading as %lu/%s", filename, (unsigned long)folderid, name);
|
|
psync_apipool_release(api);
|
|
goto errs;
|
|
}
|
|
if (asize){
|
|
psync_upload_sub_bytes_uploaded(asize);
|
|
asize=0;
|
|
}
|
|
return large_upload_save(api, uploadid, folderid, name, taskid, writeid, 1, 0, key, filename);
|
|
ret01:
|
|
psync_file_close(fd);
|
|
ret0:
|
|
psync_apipool_release(api);
|
|
perm_fail_upload_task(taskid);
|
|
if (asize)
|
|
psync_upload_sub_bytes_uploaded(asize);
|
|
return 0;
|
|
err2:
|
|
psync_free(buff);
|
|
err1:
|
|
psync_file_close(fd);
|
|
err0:
|
|
psync_apipool_release_bad(api);
|
|
errs:
|
|
if (asize)
|
|
psync_upload_sub_bytes_uploaded(asize);
|
|
return PRINT_RETURN(-1);
|
|
}
|
|
|
|
static int64_t i64min(int64_t a, int64_t b){
|
|
return a<b?a:b;
|
|
}
|
|
|
|
static int upload_modify_send_copy_from(psync_socket *api, psync_uploadid_t uploadid, uint64_t offset, uint64_t length,
|
|
psync_fileid_t fileid, uint64_t hash, uint64_t *upl){
|
|
binparam params[]={P_STR("auth", psync_my_auth), P_NUM("uploadoffset", offset), P_NUM("uploadid", uploadid),
|
|
P_NUM("fileid", fileid), P_NUM("hash", hash), P_NUM("offset", offset), P_NUM("count", length)};
|
|
debug(D_NOTICE, "copying %lu bytes from fileid %lu hash %lu at offset %lu", (unsigned long)length, (unsigned long)fileid, (unsigned long) hash, (unsigned long)offset);
|
|
if (unlikely_log(!send_command_no_res(api, "upload_writefromfile", params)))
|
|
return PSYNC_NET_TEMPFAIL;
|
|
else{
|
|
*upl+=length;
|
|
psync_upload_add_bytes_uploaded(length);
|
|
return PSYNC_NET_OK;
|
|
}
|
|
}
|
|
|
|
static int upload_modify_send_local(psync_socket *api, psync_uploadid_t uploadid, uint64_t offset, uint64_t length, psync_file_t fd, uint64_t *upl){
|
|
binparam params[]={P_STR("auth", psync_my_auth), P_NUM("uploadoffset", offset), P_NUM("uploadid", uploadid)};
|
|
void *buff;
|
|
uint64_t bw;
|
|
size_t rd;
|
|
ssize_t rrd;
|
|
debug(D_NOTICE, "uploading %lu byte from local file at offset %lu", (unsigned long)length, (unsigned long)offset);
|
|
if (unlikely_log(psync_file_seek(fd, offset, P_SEEK_SET)==-1) ||
|
|
unlikely_log(!do_send_command(api, "upload_write", strlen("upload_write"), params, ARRAY_SIZE(params), length, 0)))
|
|
return PSYNC_NET_TEMPFAIL;
|
|
bw=0;
|
|
|
|
buff=psync_malloc(PSYNC_COPY_BUFFER_SIZE);
|
|
while (bw<length){
|
|
if (unlikely(stop_current_upload)){
|
|
debug(D_NOTICE, "got stop");
|
|
goto err0;
|
|
}
|
|
psync_wait_statuses_array(requiredstatuses, ARRAY_SIZE(requiredstatuses));
|
|
if (length-bw>PSYNC_COPY_BUFFER_SIZE)
|
|
rd=PSYNC_COPY_BUFFER_SIZE;
|
|
else
|
|
rd=length-bw;
|
|
rrd=psync_file_read(fd, buff, rd);
|
|
if (unlikely_log(rrd<=0)){
|
|
if (rrd==0)
|
|
goto errp;
|
|
else
|
|
goto err0;
|
|
}
|
|
bw+=rrd;
|
|
if (unlikely_log(psync_socket_writeall_upload(api, buff, rrd)!=rrd))
|
|
goto err0;
|
|
*upl+=rrd;
|
|
psync_upload_add_bytes_uploaded(rrd);
|
|
|
|
}
|
|
psync_free(buff);
|
|
return PSYNC_NET_OK;
|
|
err0:
|
|
psync_free(buff);
|
|
return PSYNC_NET_TEMPFAIL;
|
|
errp:
|
|
psync_free(buff);
|
|
return PSYNC_NET_PERMFAIL;
|
|
}
|
|
|
|
static int upload_modify_read_req(psync_socket *api){
|
|
binresult *res;
|
|
uint64_t result;
|
|
res=get_result(api);
|
|
if (!res)
|
|
return PSYNC_NET_TEMPFAIL;
|
|
result=psync_find_result(res, "result", PARAM_NUM)->num;
|
|
psync_free(res);
|
|
if (result){
|
|
debug(D_WARNING, "got %lu from upload_writefromfile or upload_write", (unsigned long)result);
|
|
psync_process_api_error(result);
|
|
return psync_handle_api_result(result);
|
|
}
|
|
else
|
|
return PSYNC_NET_OK;
|
|
}
|
|
|
|
int upload_modify(uint64_t taskid, psync_folderid_t folderid, const char *name, const char *filename, const char *indexname, psync_fileid_t fileid,
|
|
uint64_t hash, uint64_t writeid, const char *key){
|
|
binparam aparams[]={P_STR("auth", psync_my_auth)};
|
|
psync_interval_tree_t *tree, *cinterval;
|
|
psync_socket *api;
|
|
binresult *res;
|
|
psync_sql_res *sql;
|
|
int64_t fsize, coff, len;
|
|
uint64_t result, asize;
|
|
psync_uploadid_t uploadid;
|
|
psync_uint_t reqs;
|
|
psync_file_t fd;
|
|
psync_fs_err_t err;
|
|
int ret;
|
|
debug(D_NOTICE, "uploading modified file %s writeid %lu as %lu/%s", filename, (unsigned long)writeid, (unsigned long)folderid, name);
|
|
asize=0;
|
|
fd=psync_file_open(indexname, P_O_RDONLY, 0);
|
|
if (unlikely(fd==INVALID_HANDLE_VALUE)){
|
|
err=psync_fs_err();
|
|
debug(D_WARNING, "can not open %s", indexname);
|
|
if (err==P_NOENT){
|
|
perm_fail_upload_task(taskid);
|
|
return 0;
|
|
}
|
|
else
|
|
return -1;
|
|
}
|
|
tree=NULL;
|
|
if (unlikely_log((fsize=psync_file_size(fd))==-1 || psync_fs_load_interval_tree(fd, fsize, &tree)==-1)){
|
|
psync_interval_tree_free(tree);
|
|
psync_file_close(fd);
|
|
return -1;
|
|
}
|
|
psync_file_close(fd);
|
|
api=psync_apipool_get();
|
|
if (unlikely_log(!api))
|
|
goto err1;
|
|
if (unlikely_log(clean_uploads_for_task(api, taskid)))
|
|
goto err2;
|
|
res=send_command(api, "upload_create", aparams);
|
|
if (unlikely_log(!res))
|
|
goto err2;
|
|
result=psync_find_result(res, "result", PARAM_NUM)->num;
|
|
if (unlikely(result)){
|
|
psync_free(res);
|
|
psync_apipool_release(api);
|
|
psync_interval_tree_free(tree);
|
|
debug(D_WARNING, "upload_create returned %lu", (unsigned long)result);
|
|
psync_process_api_error(result);
|
|
if (psync_handle_api_result(result)==PSYNC_NET_TEMPFAIL)
|
|
return -1;
|
|
else
|
|
return 0;
|
|
}
|
|
uploadid=psync_find_result(res, "uploadid", PARAM_NUM)->num;
|
|
psync_free(res);
|
|
sql=psync_sql_prep_statement("INSERT INTO fstaskupload (fstaskid, uploadid) VALUES (?, ?)");
|
|
psync_sql_bind_uint(sql, 1, taskid);
|
|
psync_sql_bind_uint(sql, 2, uploadid);
|
|
psync_sql_run_free(sql);
|
|
fd=psync_file_open(filename, P_O_RDONLY, 0);
|
|
if (unlikely(fd==INVALID_HANDLE_VALUE)){
|
|
err=psync_fs_err();
|
|
debug(D_WARNING, "can not open %s", filename);
|
|
psync_apipool_release(api);
|
|
psync_interval_tree_free(tree);
|
|
if (err==P_NOENT){
|
|
perm_fail_upload_task(taskid);
|
|
return 0;
|
|
}
|
|
else
|
|
return -1;
|
|
}
|
|
fsize=psync_file_size(fd);
|
|
if (unlikely_log(fsize==-1))
|
|
goto err3;
|
|
debug(D_NOTICE, "file size=%lu", (unsigned long)fsize);
|
|
coff=0;
|
|
reqs=0;
|
|
cinterval=psync_interval_tree_get_first(tree);
|
|
while (coff<fsize){
|
|
if (reqs && (psync_socket_pendingdata(api) || psync_select_in(&api->sock, 1, reqs>=PSYNC_MAX_PENDING_UPLOAD_REQS?PSYNC_SOCK_READ_TIMEOUT*1000:0)!=SOCKET_ERROR)){
|
|
if ((ret=upload_modify_read_req(api))){
|
|
if (unlikely_log(ret==PSYNC_NET_PERMFAIL))
|
|
perm_fail_upload_task(taskid);
|
|
goto err3;
|
|
}
|
|
else
|
|
reqs--;
|
|
}
|
|
if (cinterval){
|
|
if (cinterval->from>coff){
|
|
len=i64min(i64min(cinterval->from, fsize)-coff, PSYNC_MAX_COPY_FROM_REQ);
|
|
ret=upload_modify_send_copy_from(api, uploadid, coff, len, fileid, hash, &asize);
|
|
reqs++;
|
|
coff+=len;
|
|
}
|
|
else if (cinterval->from<=coff && cinterval->to>coff){
|
|
ret=upload_modify_send_local(api, uploadid, coff, i64min(cinterval->to, fsize)-coff, fd, &asize);
|
|
reqs++;
|
|
coff=cinterval->to;
|
|
cinterval=psync_interval_tree_get_next(cinterval);
|
|
}
|
|
else{
|
|
debug(D_BUG, "broken interval tree");
|
|
break;
|
|
}
|
|
}
|
|
else{
|
|
len=i64min(fsize-coff, PSYNC_MAX_COPY_FROM_REQ);
|
|
ret=upload_modify_send_copy_from(api, uploadid, coff, len, fileid, hash, &asize);
|
|
reqs++;
|
|
coff+=len;
|
|
}
|
|
if (ret){
|
|
if (unlikely_log(ret==PSYNC_NET_PERMFAIL))
|
|
perm_fail_upload_task(taskid);
|
|
goto err3;
|
|
}
|
|
if (unlikely(stop_current_upload)){
|
|
debug(D_NOTICE, "got stop for file %s", name);
|
|
goto err3;
|
|
}
|
|
}
|
|
psync_file_close(fd);
|
|
while (reqs--)
|
|
if ((ret=upload_modify_read_req(api))){
|
|
if (unlikely_log(ret==PSYNC_NET_PERMFAIL))
|
|
perm_fail_upload_task(taskid);
|
|
goto err2;
|
|
}
|
|
psync_interval_tree_free(tree);
|
|
psync_upload_sub_bytes_uploaded(asize);
|
|
if (psync_fs_get_file_writeid(taskid)!=writeid){
|
|
debug(D_NOTICE, "%s changed while uploading as %lu/%s", filename, (unsigned long)folderid, name);
|
|
psync_apipool_release(api);
|
|
return -1;
|
|
}
|
|
return large_upload_save(api, uploadid, folderid, name, taskid, writeid, 0, hash, key, filename);
|
|
err3:
|
|
psync_file_close(fd);
|
|
err2:
|
|
psync_apipool_release_bad(api);
|
|
err1:
|
|
psync_interval_tree_free(tree);
|
|
psync_upload_sub_bytes_uploaded(asize);
|
|
return -1;
|
|
}
|
|
|
|
static void large_upload(){
|
|
uint64_t taskid, type, writeid;
|
|
psync_uploadid_t uploadid;
|
|
psync_folderid_t folderid;
|
|
psync_fileid_t fileid;
|
|
uint64_t hash;
|
|
const char *cname;
|
|
char *name, *filename, *indexname, *key;
|
|
size_t len;
|
|
psync_sql_res *res;
|
|
psync_variant_row row;
|
|
psync_uint_row urow;
|
|
int ret;
|
|
char fileidhex[sizeof(psync_fsfileid_t)*2+2];
|
|
debug(D_NOTICE, "started");
|
|
while (1){
|
|
psync_wait_statuses_array(requiredstatuses, ARRAY_SIZE(requiredstatuses));
|
|
res=psync_sql_query("SELECT id, type, folderid, text1, text2, int1, fileid, int2 FROM fstask WHERE status=2 AND "
|
|
"type IN ("NTO_STR(PSYNC_FS_TASK_CREAT)", "NTO_STR(PSYNC_FS_TASK_MODIFY)") ORDER BY id LIMIT 1");
|
|
row=psync_sql_fetch_row(res);
|
|
if (!row){
|
|
large_upload_running=0;
|
|
current_upload_taskid=0;
|
|
psync_sql_free_result(res);
|
|
break;
|
|
}
|
|
taskid=psync_get_number(row[0]);
|
|
type=psync_get_number(row[1]);
|
|
folderid=psync_get_number(row[2]);
|
|
if (psync_is_null(row[4]))
|
|
key=NULL;
|
|
else{
|
|
cname=psync_get_lstring(row[4], &len);
|
|
len++;
|
|
key=psync_new_cnt(char, len);
|
|
memcpy(key, cname, len);
|
|
}
|
|
cname=psync_get_lstring(row[3], &len);
|
|
writeid=psync_get_number(row[5]);
|
|
fileid=psync_get_number(row[6]);
|
|
hash=psync_get_number_or_null(row[7]);
|
|
len++;
|
|
name=psync_new_cnt(char, len);
|
|
memcpy(name, cname, len);
|
|
current_upload_taskid=taskid;
|
|
stop_current_upload=0;
|
|
psync_sql_free_result(res);
|
|
psync_binhex(fileidhex, &taskid, sizeof(psync_fsfileid_t));
|
|
fileidhex[sizeof(psync_fsfileid_t)]='d';
|
|
fileidhex[sizeof(psync_fsfileid_t)+1]=0;
|
|
cname=psync_setting_get_string(_PS(fscachepath));
|
|
filename=psync_strcat(cname, PSYNC_DIRECTORY_SEPARATOR, fileidhex, NULL);
|
|
fileidhex[sizeof(psync_fsfileid_t)]='i';
|
|
indexname=psync_strcat(cname, PSYNC_DIRECTORY_SEPARATOR, fileidhex, NULL);
|
|
res=psync_sql_query_rdlock("SELECT uploadid FROM fstaskupload WHERE fstaskid=? ORDER BY uploadid DESC LIMIT 1");
|
|
psync_sql_bind_uint(res, 1, taskid);
|
|
if ((urow=psync_sql_fetch_rowint(res)))
|
|
uploadid=urow[0];
|
|
else
|
|
uploadid=0;
|
|
psync_sql_free_result(res);
|
|
psync_upload_inc_uploads();
|
|
if (type==PSYNC_FS_TASK_CREAT)
|
|
ret=large_upload_creat(taskid, folderid, name, filename, uploadid, writeid, key);
|
|
else if (type==PSYNC_FS_TASK_MODIFY)
|
|
ret=upload_modify(taskid, folderid, name, filename, indexname, fileid, hash, writeid, key);
|
|
else{
|
|
ret=0;
|
|
debug(D_BUG, "wrong type %lu for task %lu", (unsigned long)type, (unsigned long)taskid);
|
|
res=psync_sql_prep_statement("DELETE FROM fstask WHERE id=?");
|
|
psync_sql_bind_uint(res, 1, taskid);
|
|
psync_sql_run_free(res);
|
|
psync_status_recalc_to_upload_async();
|
|
}
|
|
psync_upload_dec_uploads();
|
|
if (ret){
|
|
res=psync_sql_query_rdlock("SELECT type FROM fstask WHERE id=?");
|
|
psync_sql_bind_uint(res, 1, taskid);
|
|
if ((urow=psync_sql_fetch_rowint(res)))
|
|
uploadid=urow[0];
|
|
else
|
|
uploadid=2;
|
|
if (uploadid!=2)
|
|
current_upload_taskid=0;
|
|
psync_sql_free_result(res);
|
|
if (uploadid!=2)
|
|
psync_fsupload_wake();
|
|
psync_milisleep(PSYNC_SLEEP_ON_FAILED_UPLOAD);
|
|
}
|
|
psync_free(indexname);
|
|
psync_free(filename);
|
|
psync_free(name);
|
|
psync_free(key);
|
|
}
|
|
debug(D_NOTICE, "exited");
|
|
}
|
|
|
|
static int psync_sent_task_creat_upload_large(fsupload_task_t *task){
|
|
psync_sql_res *res;
|
|
res=psync_sql_prep_statement("UPDATE fstask SET status=2 WHERE id=? AND status=0");
|
|
psync_sql_bind_uint(res, 1, task->id);
|
|
//psync_fs_uploading_openfile(task->id);
|
|
if (!large_upload_running){
|
|
large_upload_running=1;
|
|
psync_run_thread("large file fs upload", large_upload);
|
|
}
|
|
psync_sql_run_free(res);
|
|
return 0;
|
|
}
|
|
|
|
void psync_fsupload_stop_upload_locked(uint64_t taskid){
|
|
psync_sql_res *res;
|
|
if (current_upload_taskid==taskid)
|
|
stop_current_upload=1;
|
|
res=psync_sql_prep_statement("UPDATE fstask SET status=1 WHERE id=?");
|
|
psync_sql_bind_uint(res, 1, taskid);
|
|
psync_sql_run_free(res);
|
|
assertw(psync_sql_affected_rows());
|
|
}
|
|
|
|
int psync_fsupload_in_current_small_uploads_batch_locked(uint64_t taskid){
|
|
fsupload_task_t *task;
|
|
if (!current_upload_batch)
|
|
return 0;
|
|
psync_list_for_each_element(task, current_upload_batch, fsupload_task_t, list)
|
|
if (task->id==taskid && task->type==PSYNC_FS_TASK_CREAT){
|
|
char fileidhex[sizeof(psync_fsfileid_t)*2+2];
|
|
char *filename;
|
|
psync_stat_t st;
|
|
int stret;
|
|
psync_binhex(fileidhex, &task->id, sizeof(psync_fsfileid_t));
|
|
fileidhex[sizeof(psync_fsfileid_t)]='d';
|
|
fileidhex[sizeof(psync_fsfileid_t)+1]=0;
|
|
filename=psync_strcat(psync_setting_get_string(_PS(fscachepath)), PSYNC_DIRECTORY_SEPARATOR, fileidhex, NULL);
|
|
stret=psync_stat(filename, &st);
|
|
if (stret)
|
|
debug(D_WARNING, "can not stat %s", filename);
|
|
psync_free(filename);
|
|
if (stret)
|
|
return 1;
|
|
if (psync_stat_size(&st)>PSYNC_FS_DIRECT_UPLOAD_LIMIT)
|
|
return 0;
|
|
else
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int psync_send_task_creat(psync_socket *api, fsupload_task_t *task){
|
|
if (api){
|
|
char fileidhex[sizeof(psync_fsfileid_t)*2+2];
|
|
char *filename;
|
|
psync_stat_t st;
|
|
uint64_t size;
|
|
psync_file_t fd;
|
|
int ret;
|
|
psync_binhex(fileidhex, &task->id, sizeof(psync_fsfileid_t));
|
|
fileidhex[sizeof(psync_fsfileid_t)]='d';
|
|
fileidhex[sizeof(psync_fsfileid_t)+1]=0;
|
|
filename=psync_strcat(psync_setting_get_string(_PS(fscachepath)), PSYNC_DIRECTORY_SEPARATOR, fileidhex, NULL);
|
|
fd=psync_file_open(filename, P_O_RDONLY, 0);
|
|
psync_free(filename);
|
|
if (unlikely_log(fd==INVALID_HANDLE_VALUE) || unlikely_log(psync_fstat(fd, &st))){
|
|
if (fd!=INVALID_HANDLE_VALUE)
|
|
psync_file_close(fd);
|
|
perm_fail_upload_task(task->id);
|
|
return -1;
|
|
}
|
|
size=psync_stat_size(&st);
|
|
if (size>PSYNC_FS_DIRECT_UPLOAD_LIMIT){
|
|
psync_file_close(fd);
|
|
debug(D_NOTICE, "defering upload of %lu/%s due to size of %lu", (unsigned long)task->folderid, task->text1, (unsigned long)size);
|
|
return -2;
|
|
}
|
|
else{
|
|
debug(D_NOTICE, "uploading file %lu/%s pipelined due to size of %lu", (unsigned long)task->folderid, task->text1, (unsigned long)size);
|
|
ret=psync_send_task_creat_upload_small(api, task, fd, &st);
|
|
psync_file_close(fd);
|
|
if (!ret){
|
|
psync_upload_inc_uploads();
|
|
task->ccreat=1;
|
|
}
|
|
return ret;
|
|
}
|
|
}
|
|
else
|
|
return psync_sent_task_creat_upload_large(task);
|
|
}
|
|
|
|
static int psync_send_task_modify(psync_socket *api, fsupload_task_t *task){
|
|
if (api)
|
|
return -2;
|
|
else
|
|
return psync_sent_task_creat_upload_large(task);
|
|
}
|
|
|
|
static int psync_process_task_creat(fsupload_task_t *task){
|
|
uint64_t result, hash;
|
|
const binresult *meta;
|
|
psync_fileid_t fileid;
|
|
result=psync_find_result(task->res, "result", PARAM_NUM)->num;
|
|
if (result)
|
|
return handle_upload_api_error(result, task);
|
|
meta=psync_find_result(task->res, "metadata", PARAM_ARRAY)->array[0];
|
|
fileid=psync_find_result(meta, "fileid", PARAM_NUM)->num;
|
|
hash=psync_find_result(meta, "hash", PARAM_NUM)->num;
|
|
if (psync_fs_update_openfile(task->id, task->int1, fileid, hash, psync_find_result(meta, "size", PARAM_NUM)->num, psync_find_result(meta, "created", PARAM_NUM)->num)){
|
|
debug(D_NOTICE, "file %lu/%s changed while uploading, failing task", (unsigned long)task->folderid, task->text1);
|
|
return -1;
|
|
}
|
|
psync_ops_create_file_in_db(meta);
|
|
psync_fstask_file_created(task->folderid, task->id, task->text1, fileid);
|
|
if (task->text2)
|
|
set_key_for_fileid(fileid, hash, task->text2);
|
|
psync_fs_task_to_file(task->id, fileid);
|
|
task->int2=fileid;
|
|
debug(D_NOTICE, "file %lu/%s uploaded", (unsigned long)task->folderid, task->text1);
|
|
psync_sql_commit_transaction();
|
|
psync_pagecache_creat_to_pagecache(task->id, hash, 1);
|
|
psync_sql_start_transaction();
|
|
return 0;
|
|
}
|
|
|
|
static int psync_send_task_unlink(psync_socket *api, fsupload_task_t *task){
|
|
binparam params[]={P_STR("auth", psync_my_auth), P_NUM("fileid", task->fileid), P_STR("timeformat", "timestamp")};
|
|
if (!api){
|
|
debug(D_NOTICE, "cancelling task %lu", (unsigned long)task->id);
|
|
return 0;
|
|
}
|
|
if (likely_log(send_command_no_res(api, "deletefile", params)==PTR_OK))
|
|
return 0;
|
|
else
|
|
return -1;
|
|
}
|
|
|
|
static int psync_send_task_unlink_set_rev(psync_socket *api, fsupload_task_t *task){
|
|
binparam params[]={P_STR("auth", psync_my_auth), P_NUM("fileid", task->int1>0?task->int1:task->int2),
|
|
P_NUM("revisionoffileid", task->fileid), P_STR("timeformat", "timestamp")};
|
|
if (!api){
|
|
debug(D_NOTICE, "cancelling task %lu", (unsigned long)task->id);
|
|
return 0;
|
|
}
|
|
if (likely_log(send_command_no_res(api, "deletefile", params)==PTR_OK))
|
|
return 0;
|
|
else
|
|
return -1;
|
|
}
|
|
|
|
static int psync_send_task_set_fl_mod(psync_socket *api, fsupload_task_t *task){
|
|
binparam params[]={P_STR("auth", psync_my_auth), P_NUM("fileid", task->fileid), P_STR("timeformat", "timestamp"),
|
|
P_NUM("oldtm", task->int1), P_NUM("newtm", task->int2), P_BOOL("isctime", 0)};
|
|
if (!api){
|
|
debug(D_NOTICE, "cancelling task %lu", (unsigned long)task->id);
|
|
return 0;
|
|
}
|
|
if (likely_log(send_command_no_res(api, "setfilemtime", params)==PTR_OK))
|
|
return 0;
|
|
else
|
|
return -1;
|
|
}
|
|
|
|
static int psync_send_task_set_fl_cr(psync_socket *api, fsupload_task_t *task){
|
|
binparam params[]={P_STR("auth", psync_my_auth), P_NUM("fileid", task->fileid), P_STR("timeformat", "timestamp"),
|
|
P_NUM("oldtm", task->int1), P_NUM("newtm", task->int2), P_BOOL("isctime", 1)};
|
|
if (!api){
|
|
debug(D_NOTICE, "cancelling task %lu", (unsigned long)task->id);
|
|
return 0;
|
|
}
|
|
if (likely_log(send_command_no_res(api, "setfilemtime", params)==PTR_OK))
|
|
return 0;
|
|
else
|
|
return -1;
|
|
}
|
|
|
|
static int handle_unlink_api_error(uint64_t result, fsupload_task_t *task){
|
|
debug(D_ERROR, "deletefile returned error %u for fileid %lu", (unsigned)result, (unsigned long)task->fileid);
|
|
psync_process_api_error(result);
|
|
switch (result){
|
|
case 2009: /* file does not exist, kind of success */
|
|
//psync_ops_delete_file_from_db(task->fileid);
|
|
psync_fstask_file_deleted(task->folderid, task->id, task->text1);
|
|
return 0;
|
|
case 2003: /* access denied, skip*/
|
|
psync_fstask_file_deleted(task->folderid, task->id, task->text1);
|
|
return 0;
|
|
default:
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
static int psync_process_task_unlink(fsupload_task_t *task){
|
|
uint64_t result;
|
|
result=psync_find_result(task->res, "result", PARAM_NUM)->num;
|
|
if (result)
|
|
return handle_unlink_api_error(result, task);
|
|
psync_ops_delete_file_from_db(psync_find_result(task->res, "metadata", PARAM_HASH));
|
|
psync_fstask_file_deleted(task->folderid, task->id, task->text1);
|
|
debug(D_NOTICE, "file %lu/%s deleted", (unsigned long)task->folderid, task->text1);
|
|
return 0;
|
|
}
|
|
|
|
static int psync_process_task_set_fl_mod(fsupload_task_t *task){
|
|
const binresult *meta;
|
|
psync_sql_res *res;
|
|
uint64_t result;
|
|
result=psync_find_result(task->res, "result", PARAM_NUM)->num;
|
|
if (result)
|
|
return 0;
|
|
meta=psync_find_result(task->res, "metadata", PARAM_HASH);
|
|
res=psync_sql_prep_statement("UPDATE file SET ctime=?, mtime=? WHERE id=?");
|
|
psync_sql_bind_uint(res, 1, psync_find_result(meta, "created", PARAM_NUM)->num);
|
|
psync_sql_bind_uint(res, 2, psync_find_result(meta, "modified", PARAM_NUM)->num);
|
|
psync_sql_bind_uint(res, 3, task->fileid);
|
|
psync_sql_run_free(res);
|
|
return 0;
|
|
}
|
|
|
|
static int psync_send_task_rename_file(psync_socket *api, fsupload_task_t *task){
|
|
binparam params[]={P_STR("auth", psync_my_auth), P_NUM("fileid", task->fileid), P_NUM("tofolderid", task->folderid),
|
|
P_STR("toname", task->text1), P_STR("timeformat", "timestamp")};
|
|
if (likely_log(send_command_no_res(api, "renamefile", params)==PTR_OK))
|
|
return 0;
|
|
else
|
|
return -1;
|
|
}
|
|
|
|
static int psync_send_task_rename_folder(psync_socket *api, fsupload_task_t *task){
|
|
binparam params[]={P_STR("auth", psync_my_auth), P_NUM("folderid", task->sfolderid), P_NUM("tofolderid", task->folderid),
|
|
P_STR("toname", task->text1), P_STR("timeformat", "timestamp")};
|
|
if (likely_log(send_command_no_res(api, "renamefolder", params)==PTR_OK))
|
|
return 0;
|
|
else
|
|
return -1;
|
|
}
|
|
|
|
/*static fsupload_task_t *load_task(uint64_t id){
|
|
fsupload_task_t *task;
|
|
psync_sql_res *res;
|
|
psync_variant_row row;
|
|
char *end;
|
|
size_t size;
|
|
res=psync_sql_query("SELECT id, type, folderid, fileid, text1, text2, int1, int2, sfolderid FROM fstask WHERE id=?");
|
|
psync_sql_bind_uint(res, 1, id);
|
|
task=NULL;
|
|
if ((row=psync_sql_fetch_row(res))){
|
|
size=sizeof(fsupload_task_t);
|
|
if (row[4].type==PSYNC_TSTRING)
|
|
size+=row[4].length+1;
|
|
if (row[5].type==PSYNC_TSTRING)
|
|
size+=row[5].length+1;
|
|
task=(fsupload_task_t *)psync_malloc(size);
|
|
end=(char *)(task+1);
|
|
task->res=NULL;
|
|
task->id=psync_get_number(row[0]);
|
|
task->type=psync_get_number(row[1]);
|
|
task->folderid=psync_get_number(row[2]);
|
|
task->fileid=psync_get_number_or_null(row[3]);
|
|
task->sfolderid=psync_get_number_or_null(row[8]);
|
|
if (row[4].type==PSYNC_TSTRING){
|
|
memcpy(end, row[4].str, row[4].length+1);
|
|
task->text1=end;
|
|
end+=row[4].length+1;
|
|
}
|
|
else
|
|
task->text1=NULL;
|
|
if (row[5].type==PSYNC_TSTRING){
|
|
memcpy(end, row[5].str, row[5].length+1);
|
|
task->text2=end;
|
|
}
|
|
else
|
|
task->text2=NULL;
|
|
task->int1=psync_get_snumber_or_null(row[6]);
|
|
task->int2=psync_get_snumber_or_null(row[7]);
|
|
}
|
|
psync_sql_free_result(res);
|
|
return task;
|
|
}*/
|
|
|
|
static int handle_rename_file_api_error(uint64_t result, fsupload_task_t *task){
|
|
debug(D_ERROR, "renamefile returned error %u", (unsigned)result);
|
|
psync_process_api_error(result);
|
|
switch (result){
|
|
case 2009: /* file does not exist, skip */
|
|
case 2005: /* destination does not exist, skip */
|
|
case 2004: /* already exists */
|
|
case 2003: /* access denied, skip */
|
|
case 2001: /* invalid name, should not happen */
|
|
case 2008: /* overquota */
|
|
case 2049: /* Source and target are the same file */
|
|
case 2284: /* public folder can't contain download */
|
|
case 2343: /* backup folders can't contain download links */
|
|
case 2346: /* you can't place this item in backup folders */
|
|
psync_fstask_file_renamed(task->folderid, task->id, task->text1, task->int1);
|
|
return 0;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static int psync_process_task_rename_file(fsupload_task_t *task){
|
|
uint64_t result;
|
|
const binresult *meta;
|
|
result=psync_find_result(task->res, "result", PARAM_NUM)->num;
|
|
if (result && result!=2049)
|
|
return handle_rename_file_api_error(result, task);
|
|
meta=psync_find_result(task->res, "metadata", PARAM_HASH);
|
|
psync_ops_update_file_in_db(meta);
|
|
psync_fstask_file_renamed(task->folderid, task->id, task->text1, task->int1);
|
|
debug(D_NOTICE, "file %lu/%s renamed", (unsigned long)task->folderid, task->text1);
|
|
return 0;
|
|
}
|
|
|
|
static void change_folder_name(fsupload_task_t *task) {
|
|
psync_sql_res *res;
|
|
const char *et;
|
|
char *nn;
|
|
et=task->text1;
|
|
et+=strlen(et);
|
|
nn=NULL;
|
|
if (et>task->text1+2 && et[-1]==')' && isdigit(et[-2])) {
|
|
et-=3;
|
|
while (et>task->text1+2 && isdigit(et[0]))
|
|
et--;
|
|
if (et>task->text1 && et[0]=='(' && atol(et+1)<20) {
|
|
nn=psync_new_cnt(char, et-task->text1+7);
|
|
memcpy(nn, task->text1, et-task->text1);
|
|
psync_slprintf(nn+(et-task->text1), 7, " (%d)", atoi(et+1)+1);
|
|
}
|
|
}
|
|
if (!nn) {
|
|
nn=psync_strcat(task->text1, "(1)", NULL);
|
|
}
|
|
res=psync_sql_prep_statement("UPDATE fstask SET text1=? WHERE id=?");
|
|
psync_sql_bind_string(res, 1, nn);
|
|
psync_sql_bind_uint(res, 2, task->id);
|
|
psync_sql_run_free(res);
|
|
debug(D_NOTICE, "changed target name of task %lu from %s to %s", (unsigned long)task->id, task->text1, nn);
|
|
psync_free(nn);
|
|
}
|
|
|
|
static int handle_rename_folder_api_error(uint64_t result, fsupload_task_t *task){
|
|
debug(D_ERROR, "renamefolder returned error %u parentfolderid=%lu name=%s", (unsigned)result, (unsigned long)task->folderid, task->text1);
|
|
|
|
psync_process_api_error(result);
|
|
|
|
switch (result){
|
|
case 2005: /* folder does not exist, skip */
|
|
case 2042: /* moving root, should not happen */
|
|
case 2003: /* access denied, skip */
|
|
case 2001: /* invalid name, should not happen */
|
|
case 2008: /* overquota */
|
|
case 2023: /* moving into shared folder */
|
|
case 2043: /* into itself or child */
|
|
case 2282: /* public folder can't contain shared folder */
|
|
case 2283: /* public folder can't contain upload link */
|
|
case 2284: /* public folder can't contain download link */
|
|
case 2285: /* shared folder can't contain public folder */
|
|
case 2340: /* backup folders can't contain shared folders */
|
|
case 2342: /* backup folders can't contain upload links */
|
|
case 2343: /* backup folders can't contain download links */
|
|
case 2346: /* you can't place this item in backup folder */
|
|
psync_fstask_folder_renamed(task->folderid, task->id, task->text1, task->int1);
|
|
return 0;
|
|
case 2004: /* destination folder already exists */
|
|
change_folder_name(task);
|
|
upload_wakes++;
|
|
return -1;
|
|
case BEAPI_ERR_MV_TOO_MANY_IN_SHA: /* Attempt to move more files to a shared folder than the set limit */
|
|
debug(D_ERROR, "Error 2352. Tried to move too many folders into a sahred folder at once.");
|
|
psync_fstask_folder_renamed(task->folderid, task->id, task->text1, task->int1);
|
|
psync_send_eventid(PEVENT_SHARE_RENAME_F);
|
|
return 0;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
static int psync_process_task_rename_folder(fsupload_task_t *task){
|
|
uint64_t result;
|
|
const binresult *meta;
|
|
|
|
result=psync_find_result(task->res, "result", PARAM_NUM)->num;
|
|
|
|
if (result)
|
|
return handle_rename_folder_api_error(result, task);
|
|
|
|
meta=psync_find_result(task->res, "metadata", PARAM_HASH);
|
|
psync_ops_update_folder_in_db(meta);
|
|
psync_fstask_folder_renamed(task->folderid, task->id, task->text1, task->int1);
|
|
debug(D_NOTICE, "folder %lu/%s renamed", (unsigned long)task->folderid, task->text1);
|
|
return 0;
|
|
}
|
|
|
|
static void psync_delete_write_cache_file(uint64_t taskid, int index){
|
|
char *filename;
|
|
const char *cachepath;
|
|
char fileidhex[sizeof(psync_fsfileid_t)*2+2];
|
|
psync_binhex(fileidhex, &taskid, sizeof(psync_fsfileid_t));
|
|
fileidhex[sizeof(psync_fsfileid_t)]='d';
|
|
fileidhex[sizeof(psync_fsfileid_t)+1]=0;
|
|
cachepath=psync_setting_get_string(_PS(fscachepath));
|
|
filename=psync_strcat(cachepath, PSYNC_DIRECTORY_SEPARATOR, fileidhex, NULL);
|
|
assertw(psync_file_delete(filename)==0);
|
|
psync_free(filename);
|
|
if (index){
|
|
fileidhex[sizeof(psync_fsfileid_t)]='i';
|
|
filename=psync_strcat(cachepath, PSYNC_DIRECTORY_SEPARATOR, fileidhex, NULL);
|
|
assertw(psync_file_delete(filename)==0);
|
|
psync_free(filename);
|
|
}
|
|
}
|
|
|
|
static int psync_cancel_task_creat(fsupload_task_t *task){
|
|
psync_sql_res *res;
|
|
psync_uint_row row;
|
|
psync_fileid_t fileid;
|
|
psync_delete_write_cache_file(task->id, 0);
|
|
psync_fstask_file_created(task->folderid, task->id, task->text1, 0);
|
|
res=psync_sql_query_rdlock("SELECT fileid FROM fstaskfileid WHERE fstaskid=?");
|
|
psync_sql_bind_uint(res, 1, task->id);
|
|
if ((row=psync_sql_fetch_rowint(res)))
|
|
fileid=row[0];
|
|
else
|
|
fileid=0;
|
|
psync_sql_free_result(res);
|
|
if (fileid){
|
|
debug(D_NOTICE, "cancelled creat task %lu for file %s, changed to fileid %lu", (unsigned long)task->id, task->text1, (unsigned long)fileid);
|
|
res=psync_sql_prep_statement("UPDATE fstask SET fileid=? WHERE fileid=?");
|
|
psync_sql_bind_uint(res, 1, fileid);
|
|
psync_sql_bind_int(res, 2, -(psync_fsfileid_t)task->id);
|
|
psync_sql_run_free(res);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int psync_cancel_task_rename_file(fsupload_task_t *task){
|
|
psync_fstask_file_renamed(task->folderid, task->id, task->text1, task->int1);
|
|
debug(D_NOTICE, "cancelled rename task %lu (from task %lu) for file %s", (unsigned long)task->id, (unsigned long)task->int1, task->text1);
|
|
return 0;
|
|
}
|
|
|
|
static int psync_cancel_task_modify(fsupload_task_t *task){
|
|
psync_sql_res *res;
|
|
psync_delete_write_cache_file(task->id, 1);
|
|
psync_fstask_file_modified(task->folderid, task->id, task->text1, 0);
|
|
res=psync_sql_prep_statement("UPDATE fstask SET fileid=? WHERE fileid=?");
|
|
psync_sql_bind_uint(res, 1, task->fileid);
|
|
psync_sql_bind_int(res, 2, -(psync_fsfileid_t)task->id);
|
|
psync_sql_run_free(res);
|
|
debug(D_NOTICE, "cancelled modify task %lu for file %s, changed to fileid %lu", (unsigned long)task->id, task->text1, (unsigned long)task->fileid);
|
|
return 0;
|
|
}
|
|
|
|
static int psync_cancel_task_unlink(fsupload_task_t *task){
|
|
psync_sql_res *res;
|
|
// psync_uint_row row;
|
|
if (unlikely_log((psync_fsfileid_t)task->fileid>0)){
|
|
res=psync_sql_prep_statement("UPDATE fstask SET status=0 WHERE id=?");
|
|
psync_sql_bind_uint(res, 1, task->id);
|
|
upload_wakes++;
|
|
psync_sql_run_free(res);
|
|
return -1;
|
|
}
|
|
if (task->int2){
|
|
debug(D_NOTICE, "requested cancel of delete of a modified file, deleting fileid %ld instead for file %s", (long)task->int2, task->text1);
|
|
res=psync_sql_prep_statement("UPDATE fstask SET status=0, fileid=int2 WHERE id=?");
|
|
psync_sql_bind_uint(res, 1, task->id);
|
|
upload_wakes++;
|
|
psync_sql_run_free(res);
|
|
return -1;
|
|
}
|
|
psync_fstask_file_deleted(task->folderid, task->id, task->text1);
|
|
return 0;
|
|
}
|
|
|
|
static int psync_cancel_task_unlink_set_rev(fsupload_task_t *task){
|
|
psync_sql_res *res;
|
|
if (task->int2){
|
|
debug(D_NOTICE, "converting cancelled unlink_set_rev task %lu to a normal unlink task for file %s", (unsigned long)task->id, task->text1);
|
|
res=psync_sql_prep_statement("UPDATE fstask SET fileid=int2, status=0, type="NTO_STR(PSYNC_FS_TASK_UNLINK)" WHERE id=?");
|
|
psync_sql_bind_uint(res, 1, task->id);
|
|
upload_wakes++;
|
|
psync_sql_run_free(res);
|
|
return -1;
|
|
}
|
|
psync_fstask_file_deleted(task->folderid, task->id, task->text1);
|
|
return 0;
|
|
}
|
|
|
|
typedef int (*psync_send_task_ptr)(psync_socket *, fsupload_task_t *);
|
|
typedef int (*psync_process_task_ptr)(fsupload_task_t *);
|
|
typedef int (*psync_cancel_task_ptr)(fsupload_task_t *);
|
|
|
|
static psync_send_task_ptr psync_send_task_func[]={
|
|
NULL,
|
|
psync_send_task_mkdir,
|
|
psync_send_task_rmdir,
|
|
psync_send_task_creat,
|
|
psync_send_task_unlink,
|
|
NULL,
|
|
psync_send_task_rename_file,
|
|
NULL,
|
|
psync_send_task_rename_folder,
|
|
psync_send_task_modify,
|
|
psync_send_task_unlink_set_rev,
|
|
psync_send_task_set_fl_mod,
|
|
psync_send_task_set_fl_cr
|
|
};
|
|
|
|
static psync_process_task_ptr psync_process_task_func[]={
|
|
NULL,
|
|
psync_process_task_mkdir,
|
|
psync_process_task_rmdir,
|
|
psync_process_task_creat,
|
|
psync_process_task_unlink,
|
|
NULL,
|
|
psync_process_task_rename_file,
|
|
NULL,
|
|
psync_process_task_rename_folder,
|
|
NULL,
|
|
psync_process_task_unlink,
|
|
psync_process_task_set_fl_mod,
|
|
psync_process_task_set_fl_mod
|
|
};
|
|
|
|
static psync_cancel_task_ptr psync_cancel_task_func[]={
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
psync_cancel_task_creat,
|
|
psync_cancel_task_unlink,
|
|
NULL,
|
|
psync_cancel_task_rename_file,
|
|
NULL,
|
|
NULL,
|
|
psync_cancel_task_modify,
|
|
psync_cancel_task_unlink_set_rev,
|
|
NULL,
|
|
NULL
|
|
};
|
|
|
|
static void pr_del_dep(uint64_t taskid){
|
|
psync_sql_res *res;
|
|
res=psync_sql_prep_statement("DELETE FROM fstaskdepend WHERE dependfstaskid=?");
|
|
psync_sql_bind_uint(res, 1, taskid);
|
|
psync_sql_run_free(res);
|
|
if (psync_sql_affected_rows())
|
|
upload_wakes++;
|
|
}
|
|
|
|
static void pr_del_task(uint64_t taskid){
|
|
psync_sql_res *res;
|
|
res=psync_sql_prep_statement("DELETE FROM fstask WHERE id=?");
|
|
psync_sql_bind_uint(res, 1, taskid);
|
|
psync_sql_run_free(res);
|
|
}
|
|
|
|
/*static void pr_set_task_status3(uint64_t taskid){
|
|
psync_sql_res *res;
|
|
res=psync_sql_prep_statement("UPDATE fstask SET status=3 WHERE id=?");
|
|
psync_sql_bind_uint(res, 1, taskid);
|
|
psync_sql_run_free(res);
|
|
}*/
|
|
|
|
static void pr_update_folderid(psync_folderid_t newfolderid, psync_fsfolderid_t oldfolderid){
|
|
psync_sql_res *res;
|
|
res=psync_sql_prep_statement("UPDATE fstask SET folderid=? WHERE folderid=?");
|
|
psync_sql_bind_uint(res, 1, newfolderid);
|
|
psync_sql_bind_int(res, 2, oldfolderid);
|
|
psync_sql_run_free(res);
|
|
}
|
|
|
|
static void pr_update_sfolderid(psync_folderid_t newfolderid, psync_fsfolderid_t oldfolderid){
|
|
psync_sql_res *res;
|
|
res=psync_sql_prep_statement("UPDATE fstask SET sfolderid=? WHERE sfolderid=?");
|
|
psync_sql_bind_uint(res, 1, newfolderid);
|
|
psync_sql_bind_int(res, 2, oldfolderid);
|
|
psync_sql_run_free(res);
|
|
}
|
|
|
|
static void pr_update_fileid(psync_fileid_t newfileid, psync_fsfileid_t oldfileid){
|
|
psync_sql_res *res;
|
|
res=psync_sql_prep_statement("UPDATE fstask SET fileid=? WHERE fileid=?");
|
|
psync_sql_bind_uint(res, 1, newfileid);
|
|
psync_sql_bind_int(res, 2, oldfileid);
|
|
psync_sql_run_free(res);
|
|
}
|
|
|
|
static void psync_fsupload_process_tasks(psync_list *tasks){
|
|
fsupload_task_t *task;
|
|
uint32_t creats, cancels, dels;
|
|
creats=0;
|
|
cancels=0;
|
|
dels=0;
|
|
psync_sql_start_transaction();
|
|
psync_list_for_each_element (task, tasks, fsupload_task_t, list){
|
|
if (task->ccreat)
|
|
creats++;
|
|
if (task->status==11){
|
|
if (psync_cancel_task_func[task->type] && psync_cancel_task_func[task->type](task))
|
|
continue;
|
|
pr_del_dep(task->id);
|
|
pr_del_task(task->id);
|
|
cancels++;
|
|
}
|
|
else if (task->res){
|
|
if (psync_process_task_func[task->type](task))
|
|
debug(D_WARNING, "processing task %lu of type %lu failed", (unsigned long)task->id, (unsigned long)task->type);
|
|
else{
|
|
if (task->type==PSYNC_FS_TASK_MKDIR){
|
|
pr_update_folderid(task->int2, -(psync_fsfolderid_t)task->id);
|
|
pr_update_sfolderid(task->int2, -(psync_fsfolderid_t)task->id);
|
|
}
|
|
pr_del_dep(task->id);
|
|
if (task->type==PSYNC_FS_TASK_CREAT){
|
|
pr_update_fileid(task->int2, -(psync_fsfileid_t)task->id);
|
|
pr_del_dep(task->id);
|
|
pr_del_task(task->id);
|
|
}
|
|
else{
|
|
pr_del_task(task->id);
|
|
dels++;
|
|
}
|
|
}
|
|
psync_free(task->res);
|
|
}
|
|
}
|
|
|
|
psync_sql_commit_transaction();
|
|
|
|
if (creats){
|
|
psync_upload_dec_uploads_cnt(creats);
|
|
psync_status_recalc_to_upload_async();
|
|
}
|
|
else if (cancels || dels)
|
|
psync_status_recalc_to_upload_async();
|
|
|
|
if (dels)
|
|
psync_diff_wake();
|
|
}
|
|
|
|
static void psync_fsupload_run_tasks(psync_list *tasks){
|
|
async_result_reader reader;
|
|
psync_socket *api;
|
|
fsupload_task_t *task, *rtask;
|
|
uint32_t np;
|
|
int ret;
|
|
async_result_reader_init(&reader);
|
|
api=psync_apipool_get();
|
|
if (!api)
|
|
goto err;
|
|
rtask=psync_list_element(tasks->next, fsupload_task_t, list);
|
|
ret=0;
|
|
np=0;
|
|
psync_list_for_each_element (task, tasks, fsupload_task_t, list){
|
|
task->needprocessing=0;
|
|
if (!task->type || task->type>=ARRAY_SIZE(psync_send_task_func)){
|
|
debug(D_BUG, "bad task type %lu", (unsigned long)task->type);
|
|
continue;
|
|
}
|
|
if (task->status==11){
|
|
task->needprocessing=1;
|
|
continue;
|
|
}
|
|
ret=psync_send_task_func[task->type](api, task);
|
|
if (ret==-1)
|
|
goto err0;
|
|
else if (ret==-2){
|
|
task->needprocessing=1;
|
|
np++;
|
|
continue;
|
|
}
|
|
while (get_result_async(api, &reader)==ASYNC_RES_READY){
|
|
if (unlikely_log(!reader.result))
|
|
goto err0;
|
|
while (rtask->needprocessing){
|
|
rtask=psync_list_element(rtask->list.next, fsupload_task_t, list);
|
|
assert(&rtask->list!=tasks);
|
|
}
|
|
rtask->res=reader.result;
|
|
rtask=psync_list_element(rtask->list.next, fsupload_task_t, list);
|
|
}
|
|
}
|
|
while (rtask!=task){
|
|
if (!rtask->needprocessing){
|
|
rtask->res=get_result(api);
|
|
if (unlikely_log(!rtask->res))
|
|
goto err0;
|
|
}
|
|
rtask=psync_list_element(rtask->list.next, fsupload_task_t, list);
|
|
}
|
|
psync_apipool_release(api);
|
|
async_result_reader_destroy(&reader);
|
|
psync_fsupload_process_tasks(tasks);
|
|
if (np){
|
|
psync_sql_start_transaction();
|
|
psync_list_for_each_element (task, tasks, fsupload_task_t, list)
|
|
if (task->needprocessing && task->status!=11)
|
|
psync_send_task_func[task->type](NULL, task);
|
|
psync_sql_commit_transaction();
|
|
}
|
|
return;
|
|
err0:
|
|
psync_apipool_release_bad(api);
|
|
psync_fsupload_process_tasks(tasks);
|
|
err:
|
|
async_result_reader_destroy(&reader);
|
|
psync_timer_notify_exception();
|
|
upload_wakes++;
|
|
psync_milisleep(PSYNC_SLEEP_ON_FAILED_UPLOAD);
|
|
}
|
|
|
|
static void clean_stuck_tasks(){
|
|
psync_sql_res *res;
|
|
psync_full_result_int *fr;
|
|
uint64_t taskid;
|
|
const char *cachepath;
|
|
char *filename;
|
|
uint32_t i;
|
|
char fileidhex[sizeof(psync_fsfileid_t)*2+2];
|
|
cachepath=psync_setting_get_string(_PS(fscachepath));
|
|
res=psync_sql_query_rdlock("SELECT f.id FROM fstask f LEFT JOIN pagecachetask p ON f.id=p.taskid WHERE f.status=3");
|
|
fr=psync_sql_fetchall_int(res);
|
|
for (i=0; i<fr->rows; i++){
|
|
taskid=psync_get_result_cell(fr, i, 0);
|
|
psync_binhex(fileidhex, &taskid, sizeof(psync_fsfileid_t));
|
|
fileidhex[sizeof(psync_fsfileid_t)]='d';
|
|
fileidhex[sizeof(psync_fsfileid_t)+1]=0;
|
|
filename=psync_strcat(cachepath, PSYNC_DIRECTORY_SEPARATOR, fileidhex, NULL);
|
|
psync_file_delete(filename);
|
|
psync_free(filename);
|
|
fileidhex[sizeof(psync_fsfileid_t)]='i';
|
|
filename=psync_strcat(cachepath, PSYNC_DIRECTORY_SEPARATOR, fileidhex, NULL);
|
|
psync_file_delete(filename);
|
|
psync_free(filename);
|
|
psync_sql_start_transaction();
|
|
res=psync_sql_prep_statement("DELETE FROM fstaskdepend WHERE dependfstaskid=?");
|
|
psync_sql_bind_uint(res, 1, taskid);
|
|
psync_sql_run_free(res);
|
|
res=psync_sql_prep_statement("DELETE FROM fstask WHERE id=?");
|
|
psync_sql_bind_uint(res, 1, taskid);
|
|
psync_sql_run_free(res);
|
|
psync_sql_commit_transaction();
|
|
}
|
|
psync_free(fr);
|
|
}
|
|
|
|
static void psync_fsupload_check_tasks(){
|
|
fsupload_task_t *task;
|
|
psync_sql_res *res;
|
|
psync_variant_row row;
|
|
char *end;
|
|
psync_list tasks;
|
|
size_t size;
|
|
uint32_t cnt;
|
|
psync_list_init(&tasks);
|
|
cnt=0;
|
|
if (psync_status_get(PSTATUS_TYPE_ACCFULL)==PSTATUS_ACCFULL_QUOTAOK)
|
|
res=psync_sql_query_rdlock("SELECT f.id, f.type, f.folderid, f.fileid, f.text1, f.text2, f.int1, f.int2, f.sfolderid, f.status FROM fstask f"
|
|
" LEFT JOIN fstaskdepend d ON f.id=d.fstaskid"
|
|
" WHERE d.fstaskid IS NULL AND status IN (0, 11) ORDER BY id LIMIT "NTO_STR(PSYNC_FSUPLOAD_NUM_TASKS_PER_RUN));
|
|
else
|
|
res=psync_sql_query_rdlock("SELECT f.id, f.type, f.folderid, f.fileid, f.text1, f.text2, f.int1, f.int2, f.sfolderid, f.status FROM fstask f"
|
|
" LEFT JOIN fstaskdepend d ON f.id=d.fstaskid WHERE d.fstaskid IS NULL AND status IN (0, 11) AND f.type NOT IN ("NTO_STR(PSYNC_FS_TASK_CREAT)
|
|
", "NTO_STR(PSYNC_FS_TASK_MODIFY)") ORDER BY id LIMIT "NTO_STR(PSYNC_FSUPLOAD_NUM_TASKS_PER_RUN));
|
|
while ((row=psync_sql_fetch_row(res))){
|
|
cnt++;
|
|
if (psync_get_number(row[0])==current_upload_taskid)
|
|
continue;
|
|
size=sizeof(fsupload_task_t);
|
|
if (row[4].type==PSYNC_TSTRING)
|
|
size+=row[4].length+1;
|
|
if (row[5].type==PSYNC_TSTRING)
|
|
size+=row[5].length+1;
|
|
task=(fsupload_task_t *)psync_malloc(size);
|
|
end=(char *)(task+1);
|
|
task->res=NULL;
|
|
task->id=psync_get_number(row[0]);
|
|
task->type=psync_get_number(row[1]);
|
|
task->folderid=psync_get_number(row[2]);
|
|
task->fileid=psync_get_number_or_null(row[3]);
|
|
task->sfolderid=psync_get_number_or_null(row[8]);
|
|
task->status=psync_get_number(row[9]);
|
|
if (row[4].type==PSYNC_TSTRING){
|
|
memcpy(end, row[4].str, row[4].length+1);
|
|
task->text1=end;
|
|
end+=row[4].length+1;
|
|
}
|
|
else
|
|
task->text1=NULL;
|
|
if (row[5].type==PSYNC_TSTRING){
|
|
memcpy(end, row[5].str, row[5].length+1);
|
|
task->text2=end;
|
|
}
|
|
else
|
|
task->text2=NULL;
|
|
task->int1=psync_get_snumber_or_null(row[6]);
|
|
task->int2=psync_get_snumber_or_null(row[7]);
|
|
task->ccreat=0;
|
|
psync_list_add_tail(&tasks, &task->list);
|
|
// debug(D_NOTICE, "will process taskid %lu", (unsigned long)task->id);
|
|
}
|
|
current_upload_batch=&tasks;
|
|
psync_sql_free_result(res);
|
|
if (cnt==PSYNC_FSUPLOAD_NUM_TASKS_PER_RUN)
|
|
upload_wakes++;
|
|
if (!psync_list_isempty(&tasks))
|
|
psync_fsupload_run_tasks(&tasks);
|
|
psync_sql_lock();
|
|
current_upload_batch=NULL;
|
|
psync_sql_unlock();
|
|
psync_list_for_each_element_call(&tasks, fsupload_task_t, list, psync_free);
|
|
}
|
|
|
|
static void psync_fsupload_thread(){
|
|
int waited;
|
|
clean_stuck_tasks();
|
|
waited=0;
|
|
while (psync_do_run){
|
|
psync_wait_statuses_array(requiredstatusesnooverquota, ARRAY_SIZE(requiredstatusesnooverquota));
|
|
// it is better to sleep a bit to give a chance for events to accumulate
|
|
if (waited)
|
|
psync_milisleep(100);
|
|
psync_fsupload_check_tasks();
|
|
pthread_mutex_lock(&upload_mutex);
|
|
while (!upload_wakes){
|
|
pthread_cond_wait(&upload_cond, &upload_mutex);
|
|
waited=1;
|
|
}
|
|
upload_wakes=0;
|
|
pthread_mutex_unlock(&upload_mutex);
|
|
}
|
|
}
|
|
|
|
void psync_fsupload_init(){
|
|
psync_fs_crypto_check_logs();
|
|
psync_timer_exception_handler(psync_fsupload_wake);
|
|
psync_run_thread("fsupload main", psync_fsupload_thread);
|
|
}
|
|
|
|
void psync_fsupload_wake(){
|
|
pthread_mutex_lock(&upload_mutex);
|
|
if (!upload_wakes++)
|
|
pthread_cond_signal(&upload_cond);
|
|
pthread_mutex_unlock(&upload_mutex);
|
|
}
|