mirror of
https://github.com/haiwen/seafile.git
synced 2025-01-07 03:17:13 +08:00
Remove dependency to ccnet.
This commit is contained in:
parent
8b60a0f272
commit
2d67426c03
@ -1,12 +1,6 @@
|
||||
SUBDIRS = cdc index
|
||||
|
||||
proc_headers =
|
||||
$(addprefix processors/, \
|
||||
objecttx-common.h)
|
||||
|
||||
noinst_HEADERS = \
|
||||
unpack-trees.h \
|
||||
seaf-tree-walk.h \
|
||||
diff-simple.h \
|
||||
seafile-crypt.h \
|
||||
common.h \
|
||||
@ -15,16 +9,10 @@ noinst_HEADERS = \
|
||||
block-mgr.h \
|
||||
commit-mgr.h \
|
||||
log.h \
|
||||
object-list.h \
|
||||
vc-common.h \
|
||||
seaf-utils.h \
|
||||
obj-store.h \
|
||||
obj-backend.h \
|
||||
block-backend.h \
|
||||
block.h \
|
||||
mq-mgr.h \
|
||||
seaf-db.h \
|
||||
merge-new.h \
|
||||
block-tx-utils.h \
|
||||
curl-init.h \
|
||||
$(proc_headers)
|
||||
curl-init.h
|
||||
|
@ -4,7 +4,6 @@
|
||||
|
||||
#include "seafile-session.h"
|
||||
#include "utils.h"
|
||||
#include "seaf-utils.h"
|
||||
#include "block-mgr.h"
|
||||
#include "log.h"
|
||||
|
||||
|
@ -1,305 +0,0 @@
|
||||
#include "common.h"
|
||||
|
||||
#ifndef USE_GPL_CRYPTO
|
||||
|
||||
#define DEBUG_FLAG SEAFILE_DEBUG_TRANSFER
|
||||
#include "log.h"
|
||||
|
||||
#include "utils.h"
|
||||
#include "block-tx-utils.h"
|
||||
|
||||
/* Utility functions for block transfer protocol. */
|
||||
|
||||
/* Encryption related functions. */
|
||||
|
||||
void
|
||||
blocktx_generate_encrypt_key (unsigned char *session_key, int sk_len,
|
||||
unsigned char *key, unsigned char *iv)
|
||||
{
|
||||
EVP_BytesToKey (EVP_aes_256_cbc(), /* cipher mode */
|
||||
EVP_sha1(), /* message digest */
|
||||
NULL, /* salt */
|
||||
session_key,
|
||||
sk_len,
|
||||
3, /* iteration times */
|
||||
key, /* the derived key */
|
||||
iv); /* IV, initial vector */
|
||||
}
|
||||
|
||||
int
|
||||
blocktx_encrypt_init (EVP_CIPHER_CTX **ctx,
|
||||
const unsigned char *key,
|
||||
const unsigned char *iv)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Prepare CTX for encryption. */
|
||||
*ctx = EVP_CIPHER_CTX_new ();
|
||||
|
||||
ret = EVP_EncryptInit_ex (*ctx,
|
||||
EVP_aes_256_cbc(), /* cipher mode */
|
||||
NULL, /* engine, NULL for default */
|
||||
key, /* derived key */
|
||||
iv); /* initial vector */
|
||||
if (ret == 0) {
|
||||
EVP_CIPHER_CTX_free (*ctx);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
blocktx_decrypt_init (EVP_CIPHER_CTX **ctx,
|
||||
const unsigned char *key,
|
||||
const unsigned char *iv)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Prepare CTX for decryption. */
|
||||
*ctx = EVP_CIPHER_CTX_new ();
|
||||
|
||||
ret = EVP_DecryptInit_ex (*ctx,
|
||||
EVP_aes_256_cbc(), /* cipher mode */
|
||||
NULL, /* engine, NULL for default */
|
||||
key, /* derived key */
|
||||
iv); /* initial vector */
|
||||
if (ret == 0) {
|
||||
EVP_CIPHER_CTX_free (*ctx);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Sending frame */
|
||||
|
||||
int
|
||||
send_encrypted_data_frame_begin (evutil_socket_t data_fd,
|
||||
int frame_len)
|
||||
{
|
||||
/* Compute data size after encryption.
|
||||
* Block size is 16 bytes and AES always add one padding block.
|
||||
*/
|
||||
int enc_frame_len;
|
||||
|
||||
enc_frame_len = ((frame_len >> 4) + 1) << 4;
|
||||
enc_frame_len = htonl (enc_frame_len);
|
||||
|
||||
if (sendn (data_fd, &enc_frame_len, sizeof(int)) < 0) {
|
||||
seaf_warning ("Failed to send frame length: %s.\n",
|
||||
evutil_socket_error_to_string(evutil_socket_geterror(data_fd)));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
send_encrypted_data (EVP_CIPHER_CTX *ctx,
|
||||
evutil_socket_t data_fd,
|
||||
const void *buf, int len)
|
||||
{
|
||||
char out_buf[len + ENC_BLOCK_SIZE];
|
||||
int out_len;
|
||||
|
||||
if (EVP_EncryptUpdate (ctx,
|
||||
(unsigned char *)out_buf, &out_len,
|
||||
(unsigned char *)buf, len) == 0) {
|
||||
seaf_warning ("Failed to encrypt data.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (sendn (data_fd, out_buf, out_len) < 0) {
|
||||
seaf_warning ("Failed to write data: %s.\n",
|
||||
evutil_socket_error_to_string(evutil_socket_geterror(data_fd)));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
send_encrypted_data_frame_end (EVP_CIPHER_CTX *ctx,
|
||||
evutil_socket_t data_fd)
|
||||
{
|
||||
char out_buf[ENC_BLOCK_SIZE];
|
||||
int out_len;
|
||||
|
||||
if (EVP_EncryptFinal_ex (ctx, (unsigned char *)out_buf, &out_len) == 0) {
|
||||
seaf_warning ("Failed to encrypt data.\n");
|
||||
return -1;
|
||||
}
|
||||
if (sendn (data_fd, out_buf, out_len) < 0) {
|
||||
seaf_warning ("Failed to write data: %s.\n",
|
||||
evutil_socket_error_to_string(evutil_socket_geterror(data_fd)));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Receiving frame */
|
||||
|
||||
static int
|
||||
handle_frame_content (struct evbuffer *buf, FrameParser *parser)
|
||||
{
|
||||
char *frame;
|
||||
EVP_CIPHER_CTX *ctx;
|
||||
char *out;
|
||||
int outlen, outlen2;
|
||||
int ret = 0;
|
||||
|
||||
struct evbuffer *input = buf;
|
||||
|
||||
if (evbuffer_get_length (input) < parser->enc_frame_len)
|
||||
return 0;
|
||||
|
||||
if (parser->version == 1)
|
||||
blocktx_decrypt_init (&ctx, parser->key, parser->iv);
|
||||
else if (parser->version == 2)
|
||||
blocktx_decrypt_init (&ctx, parser->key_v2, parser->iv_v2);
|
||||
|
||||
frame = g_malloc (parser->enc_frame_len);
|
||||
out = g_malloc (parser->enc_frame_len + ENC_BLOCK_SIZE);
|
||||
|
||||
evbuffer_remove (input, frame, parser->enc_frame_len);
|
||||
|
||||
if (EVP_DecryptUpdate (ctx,
|
||||
(unsigned char *)out, &outlen,
|
||||
(unsigned char *)frame,
|
||||
parser->enc_frame_len) == 0) {
|
||||
seaf_warning ("Failed to decrypt frame content.\n");
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (EVP_DecryptFinal_ex (ctx, (unsigned char *)(out + outlen), &outlen2) == 0)
|
||||
{
|
||||
seaf_warning ("Failed to decrypt frame content.\n");
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = parser->content_cb (out, outlen + outlen2, parser->cbarg);
|
||||
|
||||
out:
|
||||
g_free (frame);
|
||||
g_free (out);
|
||||
parser->enc_frame_len = 0;
|
||||
EVP_CIPHER_CTX_free (ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
handle_one_frame (struct evbuffer *buf, FrameParser *parser)
|
||||
{
|
||||
struct evbuffer *input = buf;
|
||||
|
||||
if (!parser->enc_frame_len) {
|
||||
/* Read the length of the encrypted frame first. */
|
||||
if (evbuffer_get_length (input) < sizeof(int))
|
||||
return 0;
|
||||
|
||||
int frame_len;
|
||||
evbuffer_remove (input, &frame_len, sizeof(int));
|
||||
parser->enc_frame_len = ntohl (frame_len);
|
||||
|
||||
if (evbuffer_get_length (input) > 0)
|
||||
return handle_frame_content (buf, parser);
|
||||
|
||||
return 0;
|
||||
} else {
|
||||
return handle_frame_content (buf, parser);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
handle_frame_fragment_content (struct evbuffer *buf, FrameParser *parser)
|
||||
{
|
||||
char *fragment = NULL, *out = NULL;
|
||||
int fragment_len, outlen;
|
||||
int ret = 0;
|
||||
|
||||
struct evbuffer *input = buf;
|
||||
|
||||
fragment_len = evbuffer_get_length (input);
|
||||
fragment = g_malloc (fragment_len);
|
||||
evbuffer_remove (input, fragment, fragment_len);
|
||||
|
||||
out = g_malloc (fragment_len + ENC_BLOCK_SIZE);
|
||||
|
||||
if (EVP_DecryptUpdate (parser->ctx,
|
||||
(unsigned char *)out, &outlen,
|
||||
(unsigned char *)fragment, fragment_len) == 0) {
|
||||
seaf_warning ("Failed to decrypt frame fragment.\n");
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = parser->fragment_cb (out, outlen, 0, parser->cbarg);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
parser->remain -= fragment_len;
|
||||
|
||||
if (parser->remain <= 0) {
|
||||
if (EVP_DecryptFinal_ex (parser->ctx,
|
||||
(unsigned char *)out,
|
||||
&outlen) == 0) {
|
||||
seaf_warning ("Failed to decrypt frame fragment.\n");
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = parser->fragment_cb (out, outlen, 1, parser->cbarg);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
EVP_CIPHER_CTX_free (parser->ctx);
|
||||
parser->enc_init = FALSE;
|
||||
parser->enc_frame_len = 0;
|
||||
}
|
||||
|
||||
out:
|
||||
g_free (fragment);
|
||||
g_free (out);
|
||||
if (ret < 0) {
|
||||
EVP_CIPHER_CTX_free (parser->ctx);
|
||||
parser->enc_init = FALSE;
|
||||
parser->enc_frame_len = 0;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
handle_frame_fragments (struct evbuffer *buf, FrameParser *parser)
|
||||
{
|
||||
struct evbuffer *input = buf;
|
||||
|
||||
if (!parser->enc_frame_len) {
|
||||
/* Read the length of the encrypted frame first. */
|
||||
if (evbuffer_get_length (input) < sizeof(int))
|
||||
return 0;
|
||||
|
||||
int frame_len;
|
||||
evbuffer_remove (input, &frame_len, sizeof(int));
|
||||
parser->enc_frame_len = ntohl (frame_len);
|
||||
parser->remain = parser->enc_frame_len;
|
||||
|
||||
if (parser->version == 1)
|
||||
blocktx_decrypt_init (&parser->ctx, parser->key, parser->iv);
|
||||
else if (parser->version == 2)
|
||||
blocktx_decrypt_init (&parser->ctx, parser->key_v2, parser->iv_v2);
|
||||
parser->enc_init = TRUE;
|
||||
|
||||
if (evbuffer_get_length (input) > 0)
|
||||
return handle_frame_fragment_content (buf, parser);
|
||||
|
||||
return 0;
|
||||
} else {
|
||||
return handle_frame_fragment_content (buf, parser);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@ -1,142 +0,0 @@
|
||||
#ifndef BLOCK_TX_UTILS_H
|
||||
#define BLOCK_TX_UTILS_H
|
||||
|
||||
#include <event2/buffer.h>
|
||||
#include <event2/util.h>
|
||||
#include <openssl/evp.h>
|
||||
|
||||
/* Common structures and contants shared by the client and server. */
|
||||
|
||||
/* We use AES 256 */
|
||||
#define ENC_KEY_SIZE 32
|
||||
#define ENC_BLOCK_SIZE 16
|
||||
|
||||
#define BLOCK_PROTOCOL_VERSION 2
|
||||
|
||||
enum {
|
||||
STATUS_OK = 0,
|
||||
STATUS_VERSION_MISMATCH,
|
||||
STATUS_BAD_REQUEST,
|
||||
STATUS_ACCESS_DENIED,
|
||||
STATUS_INTERNAL_SERVER_ERROR,
|
||||
STATUS_NOT_FOUND,
|
||||
};
|
||||
|
||||
struct _HandshakeRequest {
|
||||
gint32 version;
|
||||
gint32 key_len;
|
||||
char enc_session_key[0];
|
||||
} __attribute__((__packed__));
|
||||
|
||||
typedef struct _HandshakeRequest HandshakeRequest;
|
||||
|
||||
struct _HandshakeResponse {
|
||||
gint32 status;
|
||||
gint32 version;
|
||||
} __attribute__((__packed__));
|
||||
|
||||
typedef struct _HandshakeResponse HandshakeResponse;
|
||||
|
||||
struct _AuthResponse {
|
||||
gint32 status;
|
||||
} __attribute__((__packed__));
|
||||
|
||||
typedef struct _AuthResponse AuthResponse;
|
||||
|
||||
enum {
|
||||
REQUEST_COMMAND_GET = 0,
|
||||
REQUEST_COMMAND_PUT,
|
||||
};
|
||||
|
||||
struct _RequestHeader {
|
||||
gint32 command;
|
||||
char block_id[40];
|
||||
} __attribute__((__packed__));
|
||||
|
||||
typedef struct _RequestHeader RequestHeader;
|
||||
|
||||
struct _ResponseHeader {
|
||||
gint32 status;
|
||||
} __attribute__((__packed__));
|
||||
|
||||
typedef struct _ResponseHeader ResponseHeader;
|
||||
|
||||
/* Utility functions for encryption. */
|
||||
|
||||
void
|
||||
blocktx_generate_encrypt_key (unsigned char *session_key, int sk_len,
|
||||
unsigned char *key, unsigned char *iv);
|
||||
|
||||
int
|
||||
blocktx_encrypt_init (EVP_CIPHER_CTX **ctx,
|
||||
const unsigned char *key,
|
||||
const unsigned char *iv);
|
||||
|
||||
int
|
||||
blocktx_decrypt_init (EVP_CIPHER_CTX **ctx,
|
||||
const unsigned char *key,
|
||||
const unsigned char *iv);
|
||||
|
||||
/*
|
||||
* Encrypted data is sent in "frames".
|
||||
* Format of a frame:
|
||||
*
|
||||
* length of data in the frame after encryption + encrypted data.
|
||||
*
|
||||
* Each frame can contain three types of contents:
|
||||
* 1. Auth request or response;
|
||||
* 2. Block request or response header;
|
||||
* 3. Block content.
|
||||
*/
|
||||
|
||||
int
|
||||
send_encrypted_data_frame_begin (evutil_socket_t data_fd,
|
||||
int frame_len);
|
||||
|
||||
int
|
||||
send_encrypted_data (EVP_CIPHER_CTX *ctx,
|
||||
evutil_socket_t data_fd,
|
||||
const void *buf, int len);
|
||||
|
||||
int
|
||||
send_encrypted_data_frame_end (EVP_CIPHER_CTX *ctx,
|
||||
evutil_socket_t data_fd);
|
||||
|
||||
typedef int (*FrameContentCB) (char *, int, void *);
|
||||
|
||||
typedef int (*FrameFragmentCB) (char *, int, int, void *);
|
||||
|
||||
typedef struct _FrameParser {
|
||||
int enc_frame_len;
|
||||
|
||||
unsigned char key[ENC_KEY_SIZE];
|
||||
unsigned char iv[ENC_BLOCK_SIZE];
|
||||
gboolean enc_init;
|
||||
EVP_CIPHER_CTX *ctx;
|
||||
|
||||
unsigned char key_v2[ENC_KEY_SIZE];
|
||||
unsigned char iv_v2[ENC_BLOCK_SIZE];
|
||||
|
||||
int version;
|
||||
|
||||
/* Used when parsing fragments */
|
||||
int remain;
|
||||
|
||||
FrameContentCB content_cb;
|
||||
FrameFragmentCB fragment_cb;
|
||||
void *cbarg;
|
||||
} FrameParser;
|
||||
|
||||
/* Handle entire frame all at once.
|
||||
* parser->content_cb() will be called after the entire frame is read.
|
||||
*/
|
||||
int
|
||||
handle_one_frame (struct evbuffer *buf, FrameParser *parser);
|
||||
|
||||
/* Handle a frame fragment by fragment.
|
||||
* parser->fragment_cb() will be called when any amount data is read.
|
||||
*/
|
||||
int
|
||||
handle_frame_fragments (struct evbuffer *buf, FrameParser *parser);
|
||||
|
||||
#endif
|
@ -12,7 +12,6 @@
|
||||
|
||||
#include "seafile-session.h"
|
||||
#include "commit-mgr.h"
|
||||
#include "seaf-utils.h"
|
||||
|
||||
#define MAX_TIME_SKEW 259200 /* 3 days */
|
||||
|
||||
|
@ -3,10 +3,6 @@
|
||||
#include "utils.h"
|
||||
#include "log.h"
|
||||
|
||||
#ifndef SEAFILE_SERVER
|
||||
#include "unpack-trees.h"
|
||||
#endif
|
||||
|
||||
DiffEntry *
|
||||
diff_entry_new (char type, char status, unsigned char *sha1, const char *name)
|
||||
{
|
||||
@ -36,7 +32,6 @@ diff_entry_new_from_dirent (char type, char status,
|
||||
memcpy (de->sha1, sha1, 20);
|
||||
de->name = path;
|
||||
|
||||
#ifdef SEAFILE_CLIENT
|
||||
if (type == DIFF_TYPE_COMMITS &&
|
||||
(status == DIFF_STATUS_ADDED ||
|
||||
status == DIFF_STATUS_MODIFIED ||
|
||||
@ -47,7 +42,6 @@ diff_entry_new_from_dirent (char type, char status,
|
||||
de->modifier = g_strdup(dent->modifier);
|
||||
de->size = dent->size;
|
||||
}
|
||||
#endif
|
||||
|
||||
return de;
|
||||
}
|
||||
@ -59,114 +53,11 @@ diff_entry_free (DiffEntry *de)
|
||||
if (de->new_name)
|
||||
g_free (de->new_name);
|
||||
|
||||
#ifdef SEAFILE_CLIENT
|
||||
g_free (de->modifier);
|
||||
#endif
|
||||
|
||||
g_free (de);
|
||||
}
|
||||
|
||||
#ifndef SEAFILE_SERVER
|
||||
|
||||
static void
|
||||
diff_two_cache_entries (struct cache_entry *tree1,
|
||||
struct cache_entry *tree2,
|
||||
int diff_type,
|
||||
GList **results)
|
||||
{
|
||||
DiffEntry *de;
|
||||
|
||||
if (!tree1) {
|
||||
if (S_ISDIR(tree2->ce_mode)) {
|
||||
de = diff_entry_new (diff_type, DIFF_STATUS_DIR_ADDED,
|
||||
tree2->sha1, tree2->name);
|
||||
} else {
|
||||
de = diff_entry_new (diff_type, DIFF_STATUS_ADDED,
|
||||
tree2->sha1, tree2->name);
|
||||
}
|
||||
*results = g_list_prepend (*results, de);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!tree2) {
|
||||
if (S_ISDIR(tree1->ce_mode)) {
|
||||
de = diff_entry_new (diff_type, DIFF_STATUS_DIR_DELETED,
|
||||
tree1->sha1, tree1->name);
|
||||
} else {
|
||||
de = diff_entry_new (diff_type, DIFF_STATUS_DELETED,
|
||||
tree1->sha1, tree1->name);
|
||||
}
|
||||
*results = g_list_prepend (*results, de);
|
||||
return;
|
||||
}
|
||||
|
||||
if (tree2->ce_mode != tree1->ce_mode || hashcmp(tree2->sha1, tree1->sha1) != 0) {
|
||||
if (S_ISDIR(tree2->ce_mode)) {
|
||||
de = diff_entry_new (diff_type, DIFF_STATUS_DELETED,
|
||||
tree1->sha1, tree1->name);
|
||||
*results = g_list_prepend (*results, de);
|
||||
de = diff_entry_new (diff_type, DIFF_STATUS_DIR_ADDED,
|
||||
tree2->sha1, tree2->name);
|
||||
*results = g_list_prepend (*results, de);
|
||||
} else if (S_ISDIR(tree1->ce_mode)) {
|
||||
de = diff_entry_new (diff_type, DIFF_STATUS_DIR_DELETED,
|
||||
tree1->sha1, tree1->name);
|
||||
*results = g_list_prepend (*results, de);
|
||||
de = diff_entry_new (diff_type, DIFF_STATUS_ADDED,
|
||||
tree2->sha1, tree2->name);
|
||||
*results = g_list_prepend (*results, de);
|
||||
} else {
|
||||
de = diff_entry_new (diff_type, DIFF_STATUS_MODIFIED,
|
||||
tree2->sha1, tree2->name);
|
||||
*results = g_list_prepend (*results, de);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int oneway_diff(struct cache_entry **src, struct unpack_trees_options *o)
|
||||
{
|
||||
struct cache_entry *idx = src[0];
|
||||
struct cache_entry *tree = src[1];
|
||||
GList **results = o->unpack_data;
|
||||
|
||||
if (idx == o->df_conflict_entry)
|
||||
idx = NULL;
|
||||
if (tree == o->df_conflict_entry)
|
||||
tree = NULL;
|
||||
|
||||
diff_two_cache_entries (tree, idx, DIFF_TYPE_INDEX, results);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int diff_index(const char *repo_id, int version,
|
||||
struct index_state *istate, SeafDir *root, GList **results)
|
||||
{
|
||||
struct tree_desc t;
|
||||
struct unpack_trees_options opts;
|
||||
|
||||
memset(&opts, 0, sizeof(opts));
|
||||
memcpy (opts.repo_id, repo_id, 36);
|
||||
opts.version = version;
|
||||
opts.head_idx = 1;
|
||||
opts.index_only = 1;
|
||||
/* Unmerged entries are handled in diff worktree. */
|
||||
opts.skip_unmerged = 1;
|
||||
opts.merge = 1;
|
||||
opts.fn = oneway_diff;
|
||||
opts.unpack_data = results;
|
||||
opts.src_index = istate;
|
||||
opts.dst_index = NULL;
|
||||
|
||||
fill_tree_descriptor(repo_id, version, &t, root->dir_id);
|
||||
int ret = unpack_trees(1, &t, &opts);
|
||||
|
||||
tree_desc_free (&t);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif /* not SEAFILE_SERVER */
|
||||
|
||||
inline static gboolean
|
||||
dirent_same (SeafDirent *denta, SeafDirent *dentb)
|
||||
{
|
||||
@ -456,20 +347,12 @@ diff_commits (SeafCommit *commit1, SeafCommit *commit2, GList **results,
|
||||
data.fold_dir_diff = fold_dir_diff;
|
||||
|
||||
memset (&opt, 0, sizeof(opt));
|
||||
#ifdef SEAFILE_SERVER
|
||||
memcpy (opt.store_id, repo->store_id, 36);
|
||||
#else
|
||||
memcpy (opt.store_id, repo->id, 36);
|
||||
#endif
|
||||
opt.version = repo->version;
|
||||
opt.file_cb = twoway_diff_files;
|
||||
opt.dir_cb = twoway_diff_dirs;
|
||||
opt.data = &data;
|
||||
|
||||
#ifdef SEAFILE_SERVER
|
||||
seaf_repo_unref (repo);
|
||||
#endif
|
||||
|
||||
roots[0] = commit1->root_id;
|
||||
roots[1] = commit2->root_id;
|
||||
|
||||
@ -608,20 +491,12 @@ diff_merge (SeafCommit *merge, GList **results, gboolean fold_dir_diff)
|
||||
data.fold_dir_diff = fold_dir_diff;
|
||||
|
||||
memset (&opt, 0, sizeof(opt));
|
||||
#ifdef SEAFILE_SERVER
|
||||
memcpy (opt.store_id, repo->store_id, 36);
|
||||
#else
|
||||
memcpy (opt.store_id, repo->id, 36);
|
||||
#endif
|
||||
opt.version = repo->version;
|
||||
opt.file_cb = threeway_diff_files;
|
||||
opt.dir_cb = threeway_diff_dirs;
|
||||
opt.data = &data;
|
||||
|
||||
#ifdef SEAFILE_SERVER
|
||||
seaf_repo_unref (repo);
|
||||
#endif
|
||||
|
||||
roots[0] = merge->root_id;
|
||||
roots[1] = parent1->root_id;
|
||||
roots[2] = parent2->root_id;
|
||||
|
@ -2,8 +2,6 @@
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include <ccnet.h>
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <dirent.h>
|
||||
@ -19,7 +17,6 @@
|
||||
#include "fs-mgr.h"
|
||||
#include "block-mgr.h"
|
||||
#include "utils.h"
|
||||
#include "seaf-utils.h"
|
||||
#define DEBUG_FLAG SEAFILE_DEBUG_OTHER
|
||||
#include "log.h"
|
||||
#include "../common/seafile-crypt.h"
|
||||
|
@ -1,589 +0,0 @@
|
||||
#include "seafile-session.h"
|
||||
#include "merge-new.h"
|
||||
#include "vc-common.h"
|
||||
|
||||
#define DEBUG_FLAG SEAFILE_DEBUG_MERGE
|
||||
#include "log.h"
|
||||
|
||||
static int
|
||||
merge_trees_recursive (const char *store_id, int version,
|
||||
int n, SeafDir *trees[],
|
||||
const char *basedir,
|
||||
MergeOptions *opt);
|
||||
|
||||
static char *
|
||||
merge_conflict_filename (const char *store_id, int version,
|
||||
MergeOptions *opt,
|
||||
const char *basedir,
|
||||
const char *filename)
|
||||
{
|
||||
char *path = NULL, *modifier = NULL, *conflict_name = NULL;
|
||||
gint64 mtime;
|
||||
SeafCommit *commit;
|
||||
|
||||
path = g_strconcat (basedir, filename, NULL);
|
||||
|
||||
int rc = get_file_modifier_mtime (opt->remote_repo_id,
|
||||
store_id,
|
||||
version,
|
||||
opt->remote_head,
|
||||
path,
|
||||
&modifier, &mtime);
|
||||
if (rc < 0) {
|
||||
commit = seaf_commit_manager_get_commit (seaf->commit_mgr,
|
||||
opt->remote_repo_id,
|
||||
version,
|
||||
opt->remote_head);
|
||||
if (!commit) {
|
||||
seaf_warning ("Failed to find remote head %s:%s.\n",
|
||||
opt->remote_repo_id, opt->remote_head);
|
||||
goto out;
|
||||
}
|
||||
modifier = g_strdup(commit->creator_name);
|
||||
mtime = (gint64)time(NULL);
|
||||
seaf_commit_unref (commit);
|
||||
}
|
||||
|
||||
conflict_name = gen_conflict_path (filename, modifier, mtime);
|
||||
|
||||
out:
|
||||
g_free (path);
|
||||
g_free (modifier);
|
||||
return conflict_name;
|
||||
}
|
||||
|
||||
static char *
|
||||
merge_conflict_dirname (const char *store_id, int version,
|
||||
MergeOptions *opt,
|
||||
const char *basedir,
|
||||
const char *dirname)
|
||||
{
|
||||
char *modifier = NULL, *conflict_name = NULL;
|
||||
SeafCommit *commit;
|
||||
|
||||
commit = seaf_commit_manager_get_commit (seaf->commit_mgr,
|
||||
opt->remote_repo_id, version,
|
||||
opt->remote_head);
|
||||
if (!commit) {
|
||||
seaf_warning ("Failed to find remote head %s:%s.\n",
|
||||
opt->remote_repo_id, opt->remote_head);
|
||||
goto out;
|
||||
}
|
||||
modifier = g_strdup(commit->creator_name);
|
||||
seaf_commit_unref (commit);
|
||||
|
||||
conflict_name = gen_conflict_path (dirname, modifier, (gint64)time(NULL));
|
||||
|
||||
out:
|
||||
g_free (modifier);
|
||||
return conflict_name;
|
||||
}
|
||||
|
||||
static int
|
||||
merge_entries (const char *store_id, int version,
|
||||
int n, SeafDirent *dents[],
|
||||
const char *basedir,
|
||||
GList **dents_out,
|
||||
MergeOptions *opt)
|
||||
{
|
||||
SeafDirent *files[3];
|
||||
int i;
|
||||
|
||||
memset (files, 0, sizeof(files[0])*n);
|
||||
for (i = 0; i < n; ++i) {
|
||||
if (dents[i] && S_ISREG(dents[i]->mode))
|
||||
files[i] = dents[i];
|
||||
}
|
||||
|
||||
/* If we're running 2-way merge, or the caller requires not to
|
||||
* actually merge contents, just call the callback function.
|
||||
*/
|
||||
if (n == 2 || !opt->do_merge)
|
||||
return opt->callback (basedir, files, opt);
|
||||
|
||||
/* Otherwise, we're doing a real 3-way merge of the trees.
|
||||
* It means merge files and handle any conflicts.
|
||||
*/
|
||||
|
||||
SeafDirent *base, *head, *remote;
|
||||
char *conflict_name;
|
||||
|
||||
base = files[0];
|
||||
head = files[1];
|
||||
remote = files[2];
|
||||
|
||||
if (head && remote) {
|
||||
if (strcmp (head->id, remote->id) == 0) {
|
||||
seaf_debug ("%s%s: files match\n", basedir, head->name);
|
||||
|
||||
*dents_out = g_list_prepend (*dents_out, seaf_dirent_dup(head));
|
||||
} else if (base && strcmp (base->id, head->id) == 0) {
|
||||
seaf_debug ("%s%s: unchanged in head, changed in remote\n",
|
||||
basedir, head->name);
|
||||
|
||||
*dents_out = g_list_prepend (*dents_out, seaf_dirent_dup(remote));
|
||||
} else if (base && strcmp (base->id, remote->id) == 0) {
|
||||
seaf_debug ("%s%s: unchanged in remote, changed in head\n",
|
||||
basedir, head->name);
|
||||
|
||||
*dents_out = g_list_prepend (*dents_out, seaf_dirent_dup(head));
|
||||
} else {
|
||||
/* File content conflict. */
|
||||
|
||||
seaf_debug ("%s%s: files conflict\n", basedir, head->name);
|
||||
|
||||
conflict_name = merge_conflict_filename(store_id, version,
|
||||
opt,
|
||||
basedir,
|
||||
head->name);
|
||||
if (!conflict_name)
|
||||
return -1;
|
||||
|
||||
/* Change remote entry name in place. So opt->callback
|
||||
* will see the conflict name, not the original name.
|
||||
*/
|
||||
g_free (remote->name);
|
||||
remote->name = conflict_name;
|
||||
remote->name_len = strlen (remote->name);
|
||||
|
||||
*dents_out = g_list_prepend (*dents_out, seaf_dirent_dup(head));
|
||||
*dents_out = g_list_prepend (*dents_out, seaf_dirent_dup(remote));
|
||||
|
||||
opt->conflict = TRUE;
|
||||
}
|
||||
} else if (base && !head && remote) {
|
||||
if (strcmp (base->id, remote->id) != 0) {
|
||||
if (dents[1] != NULL) {
|
||||
/* D/F conflict:
|
||||
* Head replaces file with dir, while remote change the file.
|
||||
*/
|
||||
seaf_debug ("%s%s: DFC, file -> dir, file\n",
|
||||
basedir, remote->name);
|
||||
|
||||
conflict_name = merge_conflict_filename(store_id, version,
|
||||
opt,
|
||||
basedir,
|
||||
remote->name);
|
||||
if (!conflict_name)
|
||||
return -1;
|
||||
|
||||
/* Change the name of remote, keep dir name in head unchanged.
|
||||
*/
|
||||
g_free (remote->name);
|
||||
remote->name = conflict_name;
|
||||
remote->name_len = strlen (remote->name);
|
||||
|
||||
*dents_out = g_list_prepend (*dents_out, seaf_dirent_dup(remote));
|
||||
|
||||
opt->conflict = TRUE;
|
||||
} else {
|
||||
/* Deleted in head and changed in remote. */
|
||||
|
||||
seaf_debug ("%s%s: deleted in head and changed in remote\n",
|
||||
basedir, remote->name);
|
||||
|
||||
/* Keep version of remote. */
|
||||
*dents_out = g_list_prepend (*dents_out, seaf_dirent_dup(remote));
|
||||
}
|
||||
} else {
|
||||
/* If base and remote match, the file should not be added to
|
||||
* the merge result.
|
||||
*/
|
||||
seaf_debug ("%s%s: file deleted in head, unchanged in remote\n",
|
||||
basedir, remote->name);
|
||||
}
|
||||
} else if (base && head && !remote) {
|
||||
if (strcmp (base->id, head->id) != 0) {
|
||||
if (dents[2] != NULL) {
|
||||
/* D/F conflict:
|
||||
* Remote replaces file with dir, while head change the file.
|
||||
*/
|
||||
seaf_debug ("%s%s: DFC, file -> file, dir\n",
|
||||
basedir, head->name);
|
||||
|
||||
/* We use remote head commit author name as conflict
|
||||
* suffix of a dir.
|
||||
*/
|
||||
conflict_name = merge_conflict_dirname (store_id, version,
|
||||
opt,
|
||||
basedir, dents[2]->name);
|
||||
if (!conflict_name)
|
||||
return -1;
|
||||
|
||||
/* Change remote dir name to conflict name in place. */
|
||||
g_free (dents[2]->name);
|
||||
dents[2]->name = conflict_name;
|
||||
dents[2]->name_len = strlen (dents[2]->name);
|
||||
|
||||
*dents_out = g_list_prepend (*dents_out, seaf_dirent_dup(head));
|
||||
|
||||
opt->conflict = TRUE;
|
||||
} else {
|
||||
/* Deleted in remote and changed in head. */
|
||||
|
||||
seaf_debug ("%s%s: deleted in remote and changed in head\n",
|
||||
basedir, head->name);
|
||||
|
||||
/* Keep version of remote. */
|
||||
*dents_out = g_list_prepend (*dents_out, seaf_dirent_dup(head));
|
||||
}
|
||||
} else {
|
||||
/* If base and head match, the file should not be added to
|
||||
* the merge result.
|
||||
*/
|
||||
seaf_debug ("%s%s: file deleted in remote, unchanged in head\n",
|
||||
basedir, head->name);
|
||||
}
|
||||
} else if (!base && !head && remote) {
|
||||
if (!dents[1]) {
|
||||
/* Added in remote. */
|
||||
seaf_debug ("%s%s: added in remote\n", basedir, remote->name);
|
||||
|
||||
*dents_out = g_list_prepend (*dents_out, seaf_dirent_dup(remote));
|
||||
} else if (dents[0] != NULL && strcmp(dents[0]->id, dents[1]->id) == 0) {
|
||||
/* Contents in the dir is not changed.
|
||||
* The dir will be deleted in merge_directories().
|
||||
*/
|
||||
seaf_debug ("%s%s: dir in head will be replaced by file in remote\n",
|
||||
basedir, remote->name);
|
||||
|
||||
*dents_out = g_list_prepend (*dents_out, seaf_dirent_dup(remote));
|
||||
} else {
|
||||
/* D/F conflict:
|
||||
* Contents of the dir is changed in head, while
|
||||
* remote replace the dir with a file.
|
||||
*
|
||||
* Or, head adds a new dir, while remote adds a new file,
|
||||
* with the same name.
|
||||
*/
|
||||
|
||||
seaf_debug ("%s%s: DFC, dir -> dir, file\n", basedir, remote->name);
|
||||
|
||||
conflict_name = merge_conflict_filename(store_id, version,
|
||||
opt,
|
||||
basedir,
|
||||
remote->name);
|
||||
if (!conflict_name)
|
||||
return -1;
|
||||
|
||||
g_free (remote->name);
|
||||
remote->name = conflict_name;
|
||||
remote->name_len = strlen (remote->name);
|
||||
|
||||
*dents_out = g_list_prepend (*dents_out, seaf_dirent_dup(remote));
|
||||
|
||||
opt->conflict = TRUE;
|
||||
}
|
||||
} else if (!base && head && !remote) {
|
||||
if (!dents[2]) {
|
||||
/* Added in remote. */
|
||||
seaf_debug ("%s%s: added in head\n", basedir, head->name);
|
||||
|
||||
*dents_out = g_list_prepend (*dents_out, seaf_dirent_dup(head));
|
||||
} else if (dents[0] != NULL && strcmp(dents[0]->id, dents[2]->id) == 0) {
|
||||
/* Contents in the dir is not changed.
|
||||
* The dir will be deleted in merge_directories().
|
||||
*/
|
||||
seaf_debug ("%s%s: dir in remote will be replaced by file in head\n",
|
||||
basedir, head->name);
|
||||
|
||||
*dents_out = g_list_prepend (*dents_out, seaf_dirent_dup(head));
|
||||
} else {
|
||||
/* D/F conflict:
|
||||
* Contents of the dir is changed in remote, while
|
||||
* head replace the dir with a file.
|
||||
*
|
||||
* Or, remote adds a new dir, while head adds a new file,
|
||||
* with the same name.
|
||||
*/
|
||||
|
||||
seaf_debug ("%s%s: DFC, dir -> file, dir\n", basedir, head->name);
|
||||
|
||||
conflict_name = merge_conflict_dirname (store_id, version,
|
||||
opt,
|
||||
basedir, dents[2]->name);
|
||||
if (!conflict_name)
|
||||
return -1;
|
||||
|
||||
g_free (dents[2]->name);
|
||||
dents[2]->name = conflict_name;
|
||||
dents[2]->name_len = strlen (dents[2]->name);
|
||||
|
||||
*dents_out = g_list_prepend (*dents_out, seaf_dirent_dup(head));
|
||||
|
||||
opt->conflict = TRUE;
|
||||
}
|
||||
} else if (base && !head && !remote) {
|
||||
/* Don't need to add anything to dents_out. */
|
||||
seaf_debug ("%s%s: deleted in head and remote\n", basedir, base->name);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
merge_directories (const char *store_id, int version,
|
||||
int n, SeafDirent *dents[],
|
||||
const char *basedir,
|
||||
GList **dents_out,
|
||||
MergeOptions *opt)
|
||||
{
|
||||
SeafDir *dir;
|
||||
SeafDir *sub_dirs[3];
|
||||
char *dirname = NULL;
|
||||
char *new_basedir;
|
||||
int ret = 0;
|
||||
int dir_mask = 0, i;
|
||||
SeafDirent *merged_dent;
|
||||
|
||||
for (i = 0; i < n; ++i) {
|
||||
if (dents[i] && S_ISDIR(dents[i]->mode))
|
||||
dir_mask |= 1 << i;
|
||||
}
|
||||
|
||||
seaf_debug ("dir_mask = %d\n", dir_mask);
|
||||
|
||||
if (n == 3 && opt->do_merge) {
|
||||
switch (dir_mask) {
|
||||
case 0:
|
||||
g_return_val_if_reached (-1);
|
||||
case 1:
|
||||
/* head and remote are not dirs, nothing to merge. */
|
||||
seaf_debug ("%s%s: no dir, no need to merge\n", basedir, dents[0]->name);
|
||||
return 0;
|
||||
case 2:
|
||||
/* only head is dir, add to result directly, no need to merge. */
|
||||
seaf_debug ("%s%s: only head is dir\n", basedir, dents[1]->name);
|
||||
*dents_out = g_list_prepend (*dents_out, seaf_dirent_dup(dents[1]));
|
||||
return 0;
|
||||
case 3:
|
||||
if (strcmp (dents[0]->id, dents[1]->id) == 0) {
|
||||
/* Base and head are the same, but deleted in remote. */
|
||||
seaf_debug ("%s%s: dir deleted in remote\n", basedir, dents[0]->name);
|
||||
return 0;
|
||||
}
|
||||
seaf_debug ("%s%s: dir changed in head but deleted in remote\n",
|
||||
basedir, dents[1]->name);
|
||||
break;
|
||||
case 4:
|
||||
/* only remote is dir, add to result directly, no need to merge. */
|
||||
seaf_debug ("%s%s: only remote is dir\n", basedir, dents[2]->name);
|
||||
*dents_out = g_list_prepend (*dents_out, seaf_dirent_dup(dents[2]));
|
||||
return 0;
|
||||
case 5:
|
||||
if (strcmp (dents[0]->id, dents[2]->id) == 0) {
|
||||
/* Base and remote are the same, but deleted in head. */
|
||||
seaf_debug ("%s%s: dir deleted in head\n", basedir, dents[0]->name);
|
||||
return 0;
|
||||
}
|
||||
seaf_debug ("%s%s: dir changed in remote but deleted in head\n",
|
||||
basedir, dents[2]->name);
|
||||
break;
|
||||
case 6:
|
||||
case 7:
|
||||
if (strcmp (dents[1]->id, dents[2]->id) == 0) {
|
||||
/* Head and remote match. */
|
||||
seaf_debug ("%s%s: dir is the same in head and remote\n",
|
||||
basedir, dents[1]->name);
|
||||
*dents_out = g_list_prepend (*dents_out, seaf_dirent_dup(dents[1]));
|
||||
return 0;
|
||||
} else if (dents[0] && strcmp(dents[0]->id, dents[1]->id) == 0) {
|
||||
seaf_debug ("%s%s: dir changed in remote but unchanged in head\n",
|
||||
basedir, dents[1]->name);
|
||||
*dents_out = g_list_prepend (*dents_out, seaf_dirent_dup(dents[2]));
|
||||
return 0;
|
||||
} else if (dents[0] && strcmp(dents[0]->id, dents[2]->id) == 0) {
|
||||
seaf_debug ("%s%s: dir changed in head but unchanged in remote\n",
|
||||
basedir, dents[1]->name);
|
||||
*dents_out = g_list_prepend (*dents_out, seaf_dirent_dup(dents[1]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
seaf_debug ("%s%s: dir is changed in both head and remote, "
|
||||
"merge recursively\n", basedir, dents[1]->name);
|
||||
break;
|
||||
default:
|
||||
g_return_val_if_reached (-1);
|
||||
}
|
||||
}
|
||||
|
||||
memset (sub_dirs, 0, sizeof(sub_dirs[0])*n);
|
||||
for (i = 0; i < n; ++i) {
|
||||
if (dents[i] != NULL && S_ISDIR(dents[i]->mode)) {
|
||||
dir = seaf_fs_manager_get_seafdir (seaf->fs_mgr,
|
||||
store_id, version,
|
||||
dents[i]->id);
|
||||
if (!dir) {
|
||||
seaf_warning ("Failed to find dir %s:%s.\n", store_id, dents[i]->id);
|
||||
ret = -1;
|
||||
goto free_sub_dirs;
|
||||
}
|
||||
opt->visit_dirs++;
|
||||
sub_dirs[i] = dir;
|
||||
|
||||
dirname = dents[i]->name;
|
||||
}
|
||||
}
|
||||
|
||||
new_basedir = g_strconcat (basedir, dirname, "/", NULL);
|
||||
|
||||
ret = merge_trees_recursive (store_id, version, n, sub_dirs, new_basedir, opt);
|
||||
|
||||
g_free (new_basedir);
|
||||
|
||||
if (n == 3 && opt->do_merge) {
|
||||
if (dir_mask == 3 || dir_mask == 6 || dir_mask == 7) {
|
||||
merged_dent = seaf_dirent_dup (dents[1]);
|
||||
memcpy (merged_dent->id, opt->merged_tree_root, 40);
|
||||
*dents_out = g_list_prepend (*dents_out, merged_dent);
|
||||
} else if (dir_mask == 5) {
|
||||
merged_dent = seaf_dirent_dup (dents[2]);
|
||||
memcpy (merged_dent->id, opt->merged_tree_root, 40);
|
||||
*dents_out = g_list_prepend (*dents_out, merged_dent);
|
||||
}
|
||||
}
|
||||
|
||||
free_sub_dirs:
|
||||
for (i = 0; i < n; ++i)
|
||||
seaf_dir_free (sub_dirs[i]);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static gint
|
||||
compare_dirents (gconstpointer a, gconstpointer b)
|
||||
{
|
||||
const SeafDirent *denta = a, *dentb = b;
|
||||
|
||||
return strcmp (dentb->name, denta->name);
|
||||
}
|
||||
|
||||
static int
|
||||
merge_trees_recursive (const char *store_id, int version,
|
||||
int n, SeafDir *trees[],
|
||||
const char *basedir,
|
||||
MergeOptions *opt)
|
||||
{
|
||||
GList *ptrs[3];
|
||||
SeafDirent *dents[3];
|
||||
int i;
|
||||
SeafDirent *dent;
|
||||
char *first_name;
|
||||
gboolean done;
|
||||
int ret = 0;
|
||||
SeafDir *merged_tree;
|
||||
GList *merged_dents = NULL;
|
||||
|
||||
for (i = 0; i < n; ++i) {
|
||||
if (trees[i])
|
||||
ptrs[i] = trees[i]->entries;
|
||||
else
|
||||
ptrs[i] = NULL;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
first_name = NULL;
|
||||
memset (dents, 0, sizeof(dents[0])*n);
|
||||
done = TRUE;
|
||||
|
||||
/* Find the "largest" name, assuming dirents are sorted. */
|
||||
for (i = 0; i < n; ++i) {
|
||||
if (ptrs[i] != NULL) {
|
||||
done = FALSE;
|
||||
dent = ptrs[i]->data;
|
||||
if (!first_name)
|
||||
first_name = dent->name;
|
||||
else if (strcmp(dent->name, first_name) > 0)
|
||||
first_name = dent->name;
|
||||
}
|
||||
}
|
||||
|
||||
if (done)
|
||||
break;
|
||||
|
||||
/*
|
||||
* Setup dir entries for all names that equal to first_name
|
||||
*/
|
||||
int n_files = 0, n_dirs = 0;
|
||||
for (i = 0; i < n; ++i) {
|
||||
if (ptrs[i] != NULL) {
|
||||
dent = ptrs[i]->data;
|
||||
if (strcmp(first_name, dent->name) == 0) {
|
||||
if (S_ISREG(dent->mode))
|
||||
++n_files;
|
||||
else if (S_ISDIR(dent->mode))
|
||||
++n_dirs;
|
||||
|
||||
dents[i] = dent;
|
||||
ptrs[i] = ptrs[i]->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Merge entries of this level. */
|
||||
if (n_files > 0) {
|
||||
ret = merge_entries (store_id, version,
|
||||
n, dents, basedir, &merged_dents, opt);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Recurse into sub level. */
|
||||
if (n_dirs > 0) {
|
||||
ret = merge_directories (store_id, version,
|
||||
n, dents, basedir, &merged_dents, opt);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (n == 3 && opt->do_merge) {
|
||||
merged_dents = g_list_sort (merged_dents, compare_dirents);
|
||||
merged_tree = seaf_dir_new (NULL, merged_dents,
|
||||
dir_version_from_repo_version(version));
|
||||
|
||||
memcpy (opt->merged_tree_root, merged_tree->dir_id, 40);
|
||||
|
||||
if ((trees[1] && strcmp (trees[1]->dir_id, merged_tree->dir_id) == 0) ||
|
||||
(trees[2] && strcmp (trees[2]->dir_id, merged_tree->dir_id) == 0)) {
|
||||
seaf_dir_free (merged_tree);
|
||||
} else {
|
||||
ret = seaf_dir_save (seaf->fs_mgr, store_id, version, merged_tree);
|
||||
seaf_dir_free (merged_tree);
|
||||
if (ret < 0) {
|
||||
seaf_warning ("Failed to save merged tree %s:%s.\n", store_id, basedir);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
seaf_merge_trees (const char *store_id, int version,
|
||||
int n, const char *roots[], MergeOptions *opt)
|
||||
{
|
||||
SeafDir **trees, *root;
|
||||
int i, ret;
|
||||
|
||||
g_return_val_if_fail (n == 2 || n == 3, -1);
|
||||
|
||||
trees = g_new0 (SeafDir *, n);
|
||||
for (i = 0; i < n; ++i) {
|
||||
root = seaf_fs_manager_get_seafdir (seaf->fs_mgr, store_id, version, roots[i]);
|
||||
if (!root) {
|
||||
seaf_warning ("Failed to find dir %s:%s.\n", store_id, roots[i]);
|
||||
g_free (trees);
|
||||
return -1;
|
||||
}
|
||||
trees[i] = root;
|
||||
}
|
||||
|
||||
ret = merge_trees_recursive (store_id, version, n, trees, "", opt);
|
||||
|
||||
for (i = 0; i < n; ++i)
|
||||
seaf_dir_free (trees[i]);
|
||||
g_free (trees);
|
||||
|
||||
return ret;
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
#ifndef MERGE_NEW_H
|
||||
#define MERGE_NEW_H
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include "fs-mgr.h"
|
||||
|
||||
struct MergeOptions;
|
||||
|
||||
typedef int (*MergeCallback) (const char *basedir,
|
||||
SeafDirent *dirents[],
|
||||
struct MergeOptions *opt);
|
||||
|
||||
typedef struct MergeOptions {
|
||||
int n_ways; /* only 2 and 3 way merges are supported. */
|
||||
|
||||
MergeCallback callback;
|
||||
void * data;
|
||||
|
||||
/* options only used in 3-way merge. */
|
||||
char remote_repo_id[37];
|
||||
char remote_head[41];
|
||||
gboolean do_merge; /* really merge the contents
|
||||
* and handle conflicts */
|
||||
char merged_tree_root[41]; /* merge result */
|
||||
int visit_dirs;
|
||||
gboolean conflict;
|
||||
} MergeOptions;
|
||||
|
||||
int
|
||||
seaf_merge_trees (const char *store_id, int version,
|
||||
int n, const char *roots[], MergeOptions *opt);
|
||||
|
||||
#endif
|
189
common/mq-mgr.c
189
common/mq-mgr.c
@ -1,174 +1,63 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#include <ccnet.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "log.h"
|
||||
#include "utils.h"
|
||||
#include "mq-mgr.h"
|
||||
|
||||
#include "seafile-session.h"
|
||||
#include "log.h"
|
||||
|
||||
typedef struct _SeafMqManagerPriv SeafMqManagerPriv;
|
||||
|
||||
struct _SeafMqManagerPriv {
|
||||
CcnetMqclientProc *mqclient_proc;
|
||||
CcnetTimer *timer;
|
||||
/* keep it in memory since we always use the same message */
|
||||
CcnetMessage *heartbeat_msg;
|
||||
};
|
||||
|
||||
#define HEARTBEAT_INTERVAL 2 /* 2s */
|
||||
|
||||
static int heartbeat_pulse (void *vmanager);
|
||||
typedef struct SeafMqManagerPriv {
|
||||
// chan <-> async_queue
|
||||
GHashTable *chans;
|
||||
} SeafMqManagerPriv;
|
||||
|
||||
SeafMqManager *
|
||||
seaf_mq_manager_new (SeafileSession *seaf)
|
||||
seaf_mq_manager_new ()
|
||||
{
|
||||
CcnetClient *client = seaf->session;
|
||||
SeafMqManager *mgr;
|
||||
SeafMqManagerPriv *priv;
|
||||
|
||||
mgr = g_new0 (SeafMqManager, 1);
|
||||
priv = g_new0 (SeafMqManagerPriv, 1);
|
||||
|
||||
|
||||
mgr->seaf = seaf;
|
||||
mgr->priv = priv;
|
||||
|
||||
priv->mqclient_proc = (CcnetMqclientProc *)
|
||||
ccnet_proc_factory_create_master_processor (client->proc_factory,
|
||||
"mq-client");
|
||||
|
||||
if (!priv->mqclient_proc) {
|
||||
seaf_warning ("Failed to create mqclient proc.\n");
|
||||
g_free (mgr);
|
||||
g_free(priv);
|
||||
return NULL;
|
||||
}
|
||||
SeafMqManager *mgr = g_new0 (SeafMqManager, 1);
|
||||
mgr->priv = g_new0 (SeafMqManagerPriv, 1);
|
||||
mgr->priv->chans = g_hash_table_new_full (g_str_hash, g_str_equal,
|
||||
(GDestroyNotify)g_free,
|
||||
(GDestroyNotify)g_async_queue_unref);
|
||||
|
||||
return mgr;
|
||||
}
|
||||
|
||||
static int
|
||||
start_mq_client (CcnetMqclientProc *mqclient)
|
||||
{
|
||||
if (ccnet_processor_startl ((CcnetProcessor *)mqclient, NULL) < 0) {
|
||||
ccnet_processor_done ((CcnetProcessor *)mqclient, FALSE);
|
||||
seaf_warning ("Failed to start mqclient proc\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
seaf_message ("[mq client] mq cilent is started\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
void
|
||||
seaf_mq_manager_init (SeafMqManager *mgr)
|
||||
{
|
||||
SeafMqManagerPriv *priv = mgr->priv;
|
||||
if (start_mq_client(priv->mqclient_proc) < 0)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
seaf_mq_manager_start (SeafMqManager *mgr)
|
||||
{
|
||||
SeafMqManagerPriv *priv = mgr->priv;
|
||||
priv->timer = ccnet_timer_new (heartbeat_pulse, mgr,
|
||||
HEARTBEAT_INTERVAL * 1000);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline CcnetMessage *
|
||||
create_message (SeafMqManager *mgr, const char *app, const char *body, int flags)
|
||||
{
|
||||
CcnetClient *client = mgr->seaf->session;
|
||||
CcnetMessage *msg;
|
||||
|
||||
char *from = client->base.id;
|
||||
char *to = client->base.id;
|
||||
|
||||
msg = ccnet_message_new (from, to, app, body, flags);
|
||||
return msg;
|
||||
g_hash_table_replace (mgr->priv->chans, g_strdup (SEAFILE_NOTIFY_CHAN),
|
||||
g_async_queue_new_full ((GDestroyNotify)json_decref));
|
||||
}
|
||||
|
||||
void
|
||||
seaf_mq_manager_set_heartbeat_name (SeafMqManager *mgr, const char *app)
|
||||
seaf_mq_manager_publish_notification (SeafMqManager *mgr, const char *type, const char *content)
|
||||
{
|
||||
if (!app)
|
||||
const char *chan = SEAFILE_NOTIFY_CHAN;
|
||||
GAsyncQueue *async_queue = g_hash_table_lookup (mgr->priv->chans, chan);
|
||||
if (!async_queue) {
|
||||
seaf_warning ("Unkonwn message channel %s.\n", chan);
|
||||
return;
|
||||
}
|
||||
|
||||
SeafMqManagerPriv *priv = mgr->priv;
|
||||
if (priv->heartbeat_msg)
|
||||
if (!type || !content) {
|
||||
seaf_warning ("type and content should not be NULL.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
seaf_message ("[mq mgr] publish to heartbeat mq: %s\n", app);
|
||||
json_t *msg = json_object ();
|
||||
json_object_set_new (msg, "type", json_string(type));
|
||||
json_object_set_new (msg, "content", json_string(content));
|
||||
|
||||
priv->heartbeat_msg =
|
||||
create_message (seaf->mq_mgr, app, "heartbeat", 0);
|
||||
g_async_queue_push (async_queue, msg);
|
||||
}
|
||||
|
||||
/* Wrap around ccnet_message_new since all messages we use are local. */
|
||||
static inline void
|
||||
_send_message (SeafMqManager *mgr, CcnetMessage *msg)
|
||||
json_t *
|
||||
seaf_mq_manager_pop_message (SeafMqManager *mgr)
|
||||
{
|
||||
CcnetMqclientProc *mqclient_proc = mgr->priv->mqclient_proc;
|
||||
ccnet_mqclient_proc_put_message (mqclient_proc, msg);
|
||||
}
|
||||
|
||||
void
|
||||
seaf_mq_manager_publish_message (SeafMqManager *mgr,
|
||||
CcnetMessage *msg)
|
||||
{
|
||||
_send_message (mgr, msg);
|
||||
}
|
||||
|
||||
void
|
||||
seaf_mq_manager_publish_message_full (SeafMqManager *mgr,
|
||||
const char *app,
|
||||
const char *body,
|
||||
int flags)
|
||||
{
|
||||
CcnetMessage *msg = create_message (mgr, app, body, flags);
|
||||
_send_message (mgr, msg);
|
||||
ccnet_message_free (msg);
|
||||
}
|
||||
|
||||
void
|
||||
seaf_mq_manager_publish_notification (SeafMqManager *mgr,
|
||||
const char *type,
|
||||
const char *content)
|
||||
{
|
||||
static const char *app = "seafile.notification";
|
||||
|
||||
GString *buf = g_string_new(NULL);
|
||||
g_string_append_printf (buf, "%s\n%s", type, content);
|
||||
|
||||
CcnetMessage *msg = create_message (mgr, app, buf->str, 0);
|
||||
_send_message (mgr, msg);
|
||||
|
||||
g_string_free (buf, TRUE);
|
||||
ccnet_message_free (msg);
|
||||
}
|
||||
|
||||
void
|
||||
seaf_mq_manager_publish_event (SeafMqManager *mgr, const char *content)
|
||||
{
|
||||
static const char *app = "seaf_server.event";
|
||||
|
||||
CcnetMessage *msg = create_message (mgr, app, content, 0);
|
||||
_send_message (mgr, msg);
|
||||
|
||||
ccnet_message_free (msg);
|
||||
}
|
||||
|
||||
static int
|
||||
heartbeat_pulse (void *vmanager)
|
||||
{
|
||||
SeafMqManager *mgr = vmanager;
|
||||
|
||||
_send_message (mgr, mgr->priv->heartbeat_msg);
|
||||
|
||||
return TRUE;
|
||||
const char *chan = SEAFILE_NOTIFY_CHAN;
|
||||
GAsyncQueue *async_queue = g_hash_table_lookup (mgr->priv->chans, chan);
|
||||
if (!async_queue) {
|
||||
seaf_warning ("Unkonwn message channel %s.\n", chan);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return g_async_queue_try_pop (async_queue);
|
||||
}
|
||||
|
@ -1,61 +1,24 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
/*
|
||||
* Mq-manager is responsible for:
|
||||
*
|
||||
* - Publishing heartbeat messages every HEARTBEAT_INTERVAL senconds to
|
||||
* indicate it's alive. If seafile-applet doesn't get the message, it would
|
||||
* check and try to restart seaf-daemon.
|
||||
*
|
||||
* - Provide API for other modules to publish their messages.
|
||||
*
|
||||
* Currently we publish these types of messages:
|
||||
*
|
||||
* - seafile.heartbeat <>
|
||||
* - seafile.transfer <start | stop >
|
||||
* - seafile.repo_sync_done <repo-name>
|
||||
* - seafile.promt_create_repo <worktree>
|
||||
* - seafile.repo_created <repo-name>
|
||||
*
|
||||
* And subscribe to no messages.
|
||||
*/
|
||||
|
||||
#ifndef SEAF_MQ_MANAGER_H
|
||||
#define SEAF_MQ_MANAGER_H
|
||||
|
||||
struct _CcnetMessage;
|
||||
#define SEAFILE_NOTIFY_CHAN "seafile.notification"
|
||||
|
||||
typedef struct _SeafMqManager SeafMqManager;
|
||||
struct SeafMqManagerPriv;
|
||||
|
||||
struct _SeafMqManager {
|
||||
struct _SeafileSession *seaf;
|
||||
struct _SeafMqManagerPriv *priv;
|
||||
};
|
||||
typedef struct SeafMqManager {
|
||||
struct SeafMqManagerPriv *priv;
|
||||
} SeafMqManager;
|
||||
|
||||
SeafMqManager *seaf_mq_manager_new (struct _SeafileSession *seaf);
|
||||
|
||||
void seaf_mq_manager_set_heartbeat_name (SeafMqManager *mgr, const char *app);
|
||||
|
||||
int seaf_mq_manager_init (SeafMqManager *mgr);
|
||||
|
||||
int seaf_mq_manager_start (SeafMqManager *mgr);
|
||||
|
||||
|
||||
void seaf_mq_manager_publish_message (SeafMqManager *mgr,
|
||||
struct _CcnetMessage *msg);
|
||||
SeafMqManager *
|
||||
seaf_mq_manager_new ();
|
||||
|
||||
void
|
||||
seaf_mq_manager_publish_message_full (SeafMqManager *mgr,
|
||||
const char *app,
|
||||
const char *body,
|
||||
int flags);
|
||||
seaf_mq_manager_init (SeafMqManager *mgr);
|
||||
|
||||
void
|
||||
seaf_mq_manager_publish_notification (SeafMqManager *mgr,
|
||||
const char *type,
|
||||
const char *content);
|
||||
seaf_mq_manager_publish_notification (SeafMqManager *mgr, const char *type, const char *content);
|
||||
|
||||
void
|
||||
seaf_mq_manager_publish_event (SeafMqManager *mgr, const char *content);
|
||||
json_t *
|
||||
seaf_mq_manager_pop_message (SeafMqManager *mgr);
|
||||
|
||||
#endif
|
||||
|
@ -1,148 +0,0 @@
|
||||
#include "common.h"
|
||||
#include "log.h"
|
||||
#include "obj-backend.h"
|
||||
|
||||
#ifdef RIAK_BACKEND
|
||||
|
||||
#include "riak-client.h"
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
typedef struct RiakPriv {
|
||||
const char *host;
|
||||
const char *port;
|
||||
const char *bucket;
|
||||
int n_write;
|
||||
|
||||
GQueue *conn_pool;
|
||||
pthread_mutex_t lock;
|
||||
} RiakPriv;
|
||||
|
||||
static SeafRiakClient *
|
||||
get_connection (RiakPriv *priv)
|
||||
{
|
||||
SeafRiakClient *connection;
|
||||
|
||||
pthread_mutex_lock (&priv->lock);
|
||||
|
||||
connection = g_queue_pop_head (priv->conn_pool);
|
||||
if (!connection)
|
||||
connection = seaf_riak_client_new (priv->host, priv->port);
|
||||
pthread_mutex_unlock (&priv->lock);
|
||||
return connection;
|
||||
}
|
||||
|
||||
static void
|
||||
return_connection (RiakPriv *priv, SeafRiakClient *connection)
|
||||
{
|
||||
pthread_mutex_lock (&priv->lock);
|
||||
g_queue_push_tail (priv->conn_pool, connection);
|
||||
pthread_mutex_unlock (&priv->lock);
|
||||
}
|
||||
|
||||
static int
|
||||
obj_backend_riak_read (ObjBackend *bend,
|
||||
const char *obj_id,
|
||||
void **data,
|
||||
int *len)
|
||||
{
|
||||
SeafRiakClient *conn = get_connection (bend->priv);
|
||||
RiakPriv *priv = bend->priv;
|
||||
int ret;
|
||||
|
||||
ret = seaf_riak_client_get (conn, priv->bucket, obj_id, data, len);
|
||||
|
||||
return_connection (priv, conn);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
obj_backend_riak_write (ObjBackend *bend,
|
||||
const char *obj_id,
|
||||
void *data,
|
||||
int len)
|
||||
{
|
||||
SeafRiakClient *conn = get_connection (bend->priv);
|
||||
RiakPriv *priv = bend->priv;
|
||||
int ret;
|
||||
|
||||
ret = seaf_riak_client_put (conn, priv->bucket, obj_id, data, len,
|
||||
priv->n_write);
|
||||
|
||||
return_connection (priv, conn);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
obj_backend_riak_exists (ObjBackend *bend,
|
||||
const char *obj_id)
|
||||
{
|
||||
SeafRiakClient *conn = get_connection (bend->priv);
|
||||
RiakPriv *priv = bend->priv;
|
||||
gboolean ret;
|
||||
|
||||
ret = seaf_riak_client_query (conn, priv->bucket, obj_id);
|
||||
|
||||
return_connection (priv, conn);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
obj_backend_riak_delete (ObjBackend *bend,
|
||||
const char *obj_id)
|
||||
{
|
||||
SeafRiakClient *conn = get_connection (bend->priv);
|
||||
RiakPriv *priv = bend->priv;
|
||||
|
||||
seaf_riak_client_delete (conn, priv->bucket, obj_id, priv->n_write);
|
||||
|
||||
return_connection (priv, conn);
|
||||
}
|
||||
|
||||
ObjBackend *
|
||||
obj_backend_riak_new (const char *host,
|
||||
const char *port,
|
||||
const char *bucket,
|
||||
const char *write_policy)
|
||||
{
|
||||
ObjBackend *bend;
|
||||
RiakPriv *priv;
|
||||
|
||||
bend = g_new0(ObjBackend, 1);
|
||||
priv = g_new0(RiakPriv, 1);
|
||||
bend->priv = priv;
|
||||
|
||||
priv->host = g_strdup (host);
|
||||
priv->port = g_strdup (port);
|
||||
priv->bucket = g_strdup (bucket);
|
||||
if (strcmp (write_policy, "quorum") == 0)
|
||||
priv->n_write = RIAK_QUORUM;
|
||||
else if (strcmp (write_policy, "all") == 0)
|
||||
priv->n_write = RIAK_ALL;
|
||||
else
|
||||
g_return_val_if_reached (NULL);
|
||||
|
||||
priv->conn_pool = g_queue_new ();
|
||||
pthread_mutex_init (&priv->lock, NULL);
|
||||
|
||||
bend->read = obj_backend_riak_read;
|
||||
bend->write = obj_backend_riak_write;
|
||||
bend->exists = obj_backend_riak_exists;
|
||||
bend->delete = obj_backend_riak_delete;
|
||||
|
||||
return bend;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
ObjBackend *
|
||||
obj_backend_riak_new (const char *host,
|
||||
const char *port,
|
||||
const char *bucket,
|
||||
const char *write_policy)
|
||||
{
|
||||
seaf_warning ("Riak backend is not enabled.\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif /* RIAK_BACKEND */
|
@ -2,7 +2,6 @@
|
||||
|
||||
#include "log.h"
|
||||
|
||||
#include <ccnet/cevent.h>
|
||||
#include "seafile-session.h"
|
||||
|
||||
#include "utils.h"
|
||||
@ -10,65 +9,11 @@
|
||||
#include "obj-backend.h"
|
||||
#include "obj-store.h"
|
||||
|
||||
#define MAX_READER_THREADS 2
|
||||
#define MAX_WRITER_THREADS 2
|
||||
#define MAX_STAT_THREADS 2
|
||||
|
||||
typedef struct AsyncTask {
|
||||
guint32 rw_id;
|
||||
char obj_id[41];
|
||||
void *data;
|
||||
int len;
|
||||
gboolean need_sync;
|
||||
gboolean success;
|
||||
} AsyncTask;
|
||||
|
||||
typedef struct OSCallbackStruct {
|
||||
char repo_id[37];
|
||||
int version;
|
||||
OSAsyncCallback cb;
|
||||
void *cb_data;
|
||||
} OSCallbackStruct;
|
||||
|
||||
struct SeafObjStore {
|
||||
ObjBackend *bend;
|
||||
|
||||
CEventManager *ev_mgr;
|
||||
|
||||
/* For async read. */
|
||||
guint32 next_rd_id;
|
||||
GThreadPool *read_tpool;
|
||||
GHashTable *readers;
|
||||
guint32 read_ev_id;
|
||||
|
||||
/* For async write. */
|
||||
guint32 next_wr_id;
|
||||
GThreadPool *write_tpool;
|
||||
GHashTable *writers;
|
||||
guint32 write_ev_id;
|
||||
|
||||
/* For async stat. */
|
||||
guint32 next_st_id;
|
||||
GThreadPool *stat_tpool;
|
||||
GHashTable *stats;
|
||||
guint32 stat_ev_id;
|
||||
};
|
||||
typedef struct SeafObjStore SeafObjStore;
|
||||
|
||||
static void
|
||||
reader_thread (void *data, void *user_data);
|
||||
static void
|
||||
writer_thread (void *data, void *user_data);
|
||||
static void
|
||||
stat_thread (void *data, void *user_data);
|
||||
|
||||
static void
|
||||
on_read_done (CEvent *event, void *data);
|
||||
static void
|
||||
on_write_done (CEvent *event, void *data);
|
||||
static void
|
||||
on_stat_done (CEvent *event, void *data);
|
||||
|
||||
extern ObjBackend *
|
||||
obj_backend_fs_new (const char *seaf_dir, const char *obj_type);
|
||||
|
||||
@ -90,75 +35,11 @@ seaf_obj_store_new (SeafileSession *seaf, const char *obj_type)
|
||||
return store;
|
||||
}
|
||||
|
||||
static int
|
||||
async_init (SeafObjStore *obj_store, CEventManager *ev_mgr)
|
||||
{
|
||||
GError *error = NULL;
|
||||
|
||||
obj_store->ev_mgr = ev_mgr;
|
||||
|
||||
obj_store->read_tpool = g_thread_pool_new (reader_thread,
|
||||
obj_store,
|
||||
MAX_READER_THREADS,
|
||||
FALSE,
|
||||
&error);
|
||||
if (error) {
|
||||
seaf_warning ("Failed to start reader thread pool: %s.\n", error->message);
|
||||
g_clear_error (&error);
|
||||
return -1;
|
||||
}
|
||||
|
||||
obj_store->readers = g_hash_table_new_full (g_direct_hash, g_direct_equal,
|
||||
NULL, g_free);
|
||||
obj_store->read_ev_id = cevent_manager_register (ev_mgr,
|
||||
on_read_done,
|
||||
obj_store);
|
||||
|
||||
obj_store->write_tpool = g_thread_pool_new (writer_thread,
|
||||
obj_store,
|
||||
MAX_WRITER_THREADS,
|
||||
FALSE,
|
||||
&error);
|
||||
if (error) {
|
||||
seaf_warning ("Failed to start writer thread pool: %s.\n", error->message);
|
||||
g_clear_error (&error);
|
||||
return -1;
|
||||
}
|
||||
|
||||
obj_store->writers = g_hash_table_new_full (g_direct_hash, g_direct_equal,
|
||||
NULL, g_free);
|
||||
obj_store->write_ev_id = cevent_manager_register (ev_mgr,
|
||||
on_write_done,
|
||||
obj_store);
|
||||
|
||||
obj_store->stat_tpool = g_thread_pool_new (stat_thread,
|
||||
obj_store,
|
||||
MAX_STAT_THREADS,
|
||||
FALSE,
|
||||
&error);
|
||||
if (error) {
|
||||
seaf_warning ("Failed to start statr thread pool: %s.\n", error->message);
|
||||
g_clear_error (&error);
|
||||
return -1;
|
||||
}
|
||||
|
||||
obj_store->stats = g_hash_table_new_full (g_direct_hash, g_direct_equal,
|
||||
NULL, g_free);
|
||||
obj_store->stat_ev_id = cevent_manager_register (ev_mgr,
|
||||
on_stat_done,
|
||||
obj_store);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
seaf_obj_store_init (SeafObjStore *obj_store,
|
||||
gboolean enable_async,
|
||||
CEventManager *ev_mgr)
|
||||
{
|
||||
if (enable_async && async_init (obj_store, ev_mgr) < 0)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -255,290 +136,6 @@ seaf_obj_store_copy_obj (struct SeafObjStore *obj_store,
|
||||
return bend->copy (bend, src_repo_id, src_version, dst_repo_id, dst_version, obj_id);
|
||||
}
|
||||
|
||||
static void
|
||||
reader_thread (void *data, void *user_data)
|
||||
{
|
||||
AsyncTask *task = data;
|
||||
SeafObjStore *obj_store = user_data;
|
||||
ObjBackend *bend = obj_store->bend;
|
||||
OSCallbackStruct *callback;
|
||||
|
||||
callback = g_hash_table_lookup (obj_store->readers,
|
||||
(gpointer)(long)(task->rw_id));
|
||||
if (callback) {
|
||||
task->success = TRUE;
|
||||
|
||||
if (bend->read (bend, callback->repo_id, callback->version,
|
||||
task->obj_id, &task->data, &task->len) < 0)
|
||||
task->success = FALSE;
|
||||
}
|
||||
|
||||
cevent_manager_add_event (obj_store->ev_mgr, obj_store->read_ev_id,
|
||||
task);
|
||||
}
|
||||
|
||||
static void
|
||||
stat_thread (void *data, void *user_data)
|
||||
{
|
||||
AsyncTask *task = data;
|
||||
SeafObjStore *obj_store = user_data;
|
||||
ObjBackend *bend = obj_store->bend;
|
||||
OSCallbackStruct *callback;
|
||||
|
||||
callback = g_hash_table_lookup (obj_store->stats,
|
||||
(gpointer)(long)(task->rw_id));
|
||||
if (callback) {
|
||||
task->success = TRUE;
|
||||
|
||||
if (!bend->exists (bend, callback->repo_id, callback->version, task->obj_id))
|
||||
task->success = FALSE;
|
||||
}
|
||||
|
||||
cevent_manager_add_event (obj_store->ev_mgr, obj_store->stat_ev_id,
|
||||
task);
|
||||
}
|
||||
|
||||
static void
|
||||
writer_thread (void *data, void *user_data)
|
||||
{
|
||||
AsyncTask *task = data;
|
||||
SeafObjStore *obj_store = user_data;
|
||||
ObjBackend *bend = obj_store->bend;
|
||||
OSCallbackStruct *callback;
|
||||
|
||||
callback = g_hash_table_lookup (obj_store->writers,
|
||||
(gpointer)(long)(task->rw_id));
|
||||
if (callback) {
|
||||
task->success = TRUE;
|
||||
|
||||
if (bend->write (bend, callback->repo_id, callback->version,
|
||||
task->obj_id, task->data, task->len, task->need_sync) < 0)
|
||||
task->success = FALSE;
|
||||
}
|
||||
|
||||
cevent_manager_add_event (obj_store->ev_mgr, obj_store->write_ev_id,
|
||||
task);
|
||||
}
|
||||
|
||||
static void
|
||||
on_read_done (CEvent *event, void *user_data)
|
||||
{
|
||||
AsyncTask *task = event->data;
|
||||
SeafObjStore *obj_store = user_data;
|
||||
OSCallbackStruct *callback;
|
||||
OSAsyncResult res;
|
||||
|
||||
callback = g_hash_table_lookup (obj_store->readers,
|
||||
(gpointer)(long)(task->rw_id));
|
||||
if (callback) {
|
||||
res.rw_id = task->rw_id;
|
||||
memcpy (res.obj_id, task->obj_id, 41);
|
||||
res.data = task->data;
|
||||
res.len = task->len;
|
||||
res.success = task->success;
|
||||
|
||||
callback->cb (&res, callback->cb_data);
|
||||
}
|
||||
|
||||
g_free (task->data);
|
||||
g_free (task);
|
||||
}
|
||||
|
||||
static void
|
||||
on_stat_done (CEvent *event, void *user_data)
|
||||
{
|
||||
AsyncTask *task = event->data;
|
||||
SeafObjStore *obj_store = user_data;
|
||||
OSCallbackStruct *callback;
|
||||
OSAsyncResult res;
|
||||
|
||||
callback = g_hash_table_lookup (obj_store->stats,
|
||||
(gpointer)(long)(task->rw_id));
|
||||
if (callback) {
|
||||
res.rw_id = task->rw_id;
|
||||
memcpy (res.obj_id, task->obj_id, 41);
|
||||
res.data = NULL;
|
||||
res.len = task->len;
|
||||
res.success = task->success;
|
||||
|
||||
callback->cb (&res, callback->cb_data);
|
||||
}
|
||||
|
||||
g_free (task->data);
|
||||
g_free (task);
|
||||
}
|
||||
|
||||
static void
|
||||
on_write_done (CEvent *event, void *user_data)
|
||||
{
|
||||
AsyncTask *task = event->data;
|
||||
SeafObjStore *obj_store = user_data;
|
||||
OSCallbackStruct *callback;
|
||||
OSAsyncResult res;
|
||||
|
||||
callback = g_hash_table_lookup (obj_store->writers,
|
||||
(gpointer)(long)(task->rw_id));
|
||||
if (callback) {
|
||||
res.rw_id = task->rw_id;
|
||||
memcpy (res.obj_id, task->obj_id, 41);
|
||||
res.data = task->data;
|
||||
res.len = task->len;
|
||||
res.success = task->success;
|
||||
|
||||
callback->cb (&res, callback->cb_data);
|
||||
}
|
||||
|
||||
g_free (task->data);
|
||||
g_free (task);
|
||||
}
|
||||
|
||||
guint32
|
||||
seaf_obj_store_register_async_read (struct SeafObjStore *obj_store,
|
||||
const char *repo_id,
|
||||
int version,
|
||||
OSAsyncCallback callback,
|
||||
void *cb_data)
|
||||
{
|
||||
guint32 id = obj_store->next_rd_id++;
|
||||
OSCallbackStruct *cb_struct = g_new0 (OSCallbackStruct, 1);
|
||||
|
||||
memcpy (cb_struct->repo_id, repo_id, 36);
|
||||
cb_struct->version = version;
|
||||
cb_struct->cb = callback;
|
||||
cb_struct->cb_data = cb_data;
|
||||
|
||||
g_hash_table_insert (obj_store->readers, (gpointer)(long)id, cb_struct);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
void
|
||||
seaf_obj_store_unregister_async_read (struct SeafObjStore *obj_store,
|
||||
guint32 reader_id)
|
||||
{
|
||||
g_hash_table_remove (obj_store->readers, (gpointer)(long)reader_id);
|
||||
}
|
||||
|
||||
int
|
||||
seaf_obj_store_async_read (struct SeafObjStore *obj_store,
|
||||
guint32 reader_id,
|
||||
const char *obj_id)
|
||||
{
|
||||
AsyncTask *task = g_new0 (AsyncTask, 1);
|
||||
GError *error = NULL;
|
||||
|
||||
task->rw_id = reader_id;
|
||||
memcpy (task->obj_id, obj_id, 41);
|
||||
|
||||
g_thread_pool_push (obj_store->read_tpool, task, &error);
|
||||
if (error) {
|
||||
seaf_warning ("Failed to start aysnc read of %s.\n", obj_id);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
guint32
|
||||
seaf_obj_store_register_async_stat (struct SeafObjStore *obj_store,
|
||||
const char *repo_id,
|
||||
int version,
|
||||
OSAsyncCallback callback,
|
||||
void *cb_data)
|
||||
{
|
||||
guint32 id = obj_store->next_st_id++;
|
||||
OSCallbackStruct *cb_struct = g_new0 (OSCallbackStruct, 1);
|
||||
|
||||
memcpy (cb_struct->repo_id, repo_id, 36);
|
||||
cb_struct->version = version;
|
||||
cb_struct->cb = callback;
|
||||
cb_struct->cb_data = cb_data;
|
||||
|
||||
g_hash_table_insert (obj_store->stats, (gpointer)(long)id, cb_struct);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
void
|
||||
seaf_obj_store_unregister_async_stat (struct SeafObjStore *obj_store,
|
||||
guint32 stat_id)
|
||||
{
|
||||
g_hash_table_remove (obj_store->stats, (gpointer)(long)stat_id);
|
||||
}
|
||||
|
||||
int
|
||||
seaf_obj_store_async_stat (struct SeafObjStore *obj_store,
|
||||
guint32 stat_id,
|
||||
const char *obj_id)
|
||||
{
|
||||
AsyncTask *task = g_new0 (AsyncTask, 1);
|
||||
GError *error = NULL;
|
||||
|
||||
task->rw_id = stat_id;
|
||||
memcpy (task->obj_id, obj_id, 41);
|
||||
|
||||
g_thread_pool_push (obj_store->stat_tpool, task, &error);
|
||||
if (error) {
|
||||
seaf_warning ("Failed to start aysnc stat of %s.\n", obj_id);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
guint32
|
||||
seaf_obj_store_register_async_write (struct SeafObjStore *obj_store,
|
||||
const char *repo_id,
|
||||
int version,
|
||||
OSAsyncCallback callback,
|
||||
void *cb_data)
|
||||
{
|
||||
guint32 id = obj_store->next_rd_id++;
|
||||
OSCallbackStruct *cb_struct = g_new0 (OSCallbackStruct, 1);
|
||||
|
||||
memcpy (cb_struct->repo_id, repo_id, 36);
|
||||
cb_struct->version = version;
|
||||
cb_struct->cb = callback;
|
||||
cb_struct->cb_data = cb_data;
|
||||
|
||||
g_hash_table_insert (obj_store->writers, (gpointer)(long)id, cb_struct);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
void
|
||||
seaf_obj_store_unregister_async_write (struct SeafObjStore *obj_store,
|
||||
guint32 writer_id)
|
||||
{
|
||||
g_hash_table_remove (obj_store->writers, (gpointer)(long)writer_id);
|
||||
}
|
||||
|
||||
int
|
||||
seaf_obj_store_async_write (struct SeafObjStore *obj_store,
|
||||
guint32 writer_id,
|
||||
const char *obj_id,
|
||||
const void *obj_data,
|
||||
int data_len,
|
||||
gboolean need_sync)
|
||||
{
|
||||
AsyncTask *task = g_new0 (AsyncTask, 1);
|
||||
GError *error = NULL;
|
||||
|
||||
task->rw_id = writer_id;
|
||||
memcpy (task->obj_id, obj_id, 41);
|
||||
task->data = g_memdup (obj_data, data_len);
|
||||
task->len = data_len;
|
||||
task->need_sync = need_sync;
|
||||
|
||||
g_thread_pool_push (obj_store->write_tpool, task, &error);
|
||||
if (error) {
|
||||
seaf_warning ("Failed to start aysnc write of %s.\n", obj_id);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
seaf_obj_store_remove_store (struct SeafObjStore *obj_store,
|
||||
const char *store_id)
|
||||
|
@ -67,73 +67,6 @@ seaf_obj_store_copy_obj (struct SeafObjStore *obj_store,
|
||||
int dst_version,
|
||||
const char *obj_id);
|
||||
|
||||
/* Asynchronous I/O interface. */
|
||||
|
||||
typedef struct OSAsyncResult {
|
||||
guint32 rw_id;
|
||||
char obj_id[41];
|
||||
/* @data is owned by obj-store, don't free it. */
|
||||
void *data;
|
||||
int len;
|
||||
gboolean success;
|
||||
} OSAsyncResult;
|
||||
|
||||
typedef void (*OSAsyncCallback) (OSAsyncResult *res, void *cb_data);
|
||||
|
||||
/* Async read */
|
||||
guint32
|
||||
seaf_obj_store_register_async_read (struct SeafObjStore *obj_store,
|
||||
const char *repo_id,
|
||||
int version,
|
||||
OSAsyncCallback callback,
|
||||
void *cb_data);
|
||||
|
||||
void
|
||||
seaf_obj_store_unregister_async_read (struct SeafObjStore *obj_store,
|
||||
guint32 reader_id);
|
||||
|
||||
int
|
||||
seaf_obj_store_async_read (struct SeafObjStore *obj_store,
|
||||
guint32 reader_id,
|
||||
const char *obj_id);
|
||||
|
||||
/* Async write */
|
||||
guint32
|
||||
seaf_obj_store_register_async_write (struct SeafObjStore *obj_store,
|
||||
const char *repo_id,
|
||||
int version,
|
||||
OSAsyncCallback callback,
|
||||
void *cb_data);
|
||||
|
||||
void
|
||||
seaf_obj_store_unregister_async_write (struct SeafObjStore *obj_store,
|
||||
guint32 writer_id);
|
||||
|
||||
int
|
||||
seaf_obj_store_async_write (struct SeafObjStore *obj_store,
|
||||
guint32 writer_id,
|
||||
const char *obj_id,
|
||||
const void *obj_data,
|
||||
int data_len,
|
||||
gboolean need_sync);
|
||||
|
||||
/* Async stat */
|
||||
guint32
|
||||
seaf_obj_store_register_async_stat (struct SeafObjStore *obj_store,
|
||||
const char *repo_id,
|
||||
int version,
|
||||
OSAsyncCallback callback,
|
||||
void *cb_data);
|
||||
|
||||
void
|
||||
seaf_obj_store_unregister_async_stat (struct SeafObjStore *obj_store,
|
||||
guint32 stat_id);
|
||||
|
||||
int
|
||||
seaf_obj_store_async_stat (struct SeafObjStore *obj_store,
|
||||
guint32 stat_id,
|
||||
const char *obj_id);
|
||||
|
||||
int
|
||||
seaf_obj_store_remove_store (struct SeafObjStore *obj_store,
|
||||
const char *store_id);
|
||||
|
@ -1,55 +0,0 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include "object-list.h"
|
||||
|
||||
|
||||
ObjectList *
|
||||
object_list_new ()
|
||||
{
|
||||
ObjectList *ol = g_new0 (ObjectList, 1);
|
||||
|
||||
ol->obj_hash = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL);
|
||||
ol->obj_ids = g_ptr_array_new_with_free_func (g_free);
|
||||
|
||||
return ol;
|
||||
}
|
||||
|
||||
void
|
||||
object_list_free (ObjectList *ol)
|
||||
{
|
||||
if (ol->obj_hash)
|
||||
g_hash_table_destroy (ol->obj_hash);
|
||||
g_ptr_array_free (ol->obj_ids, TRUE);
|
||||
g_free (ol);
|
||||
}
|
||||
|
||||
void
|
||||
object_list_serialize (ObjectList *ol, uint8_t **buffer, uint32_t *len)
|
||||
{
|
||||
uint32_t i;
|
||||
uint32_t offset = 0;
|
||||
uint8_t *buf;
|
||||
int ollen = object_list_length(ol);
|
||||
|
||||
buf = g_new (uint8_t, 41 * ollen);
|
||||
for (i = 0; i < ollen; ++i) {
|
||||
memcpy (&buf[offset], g_ptr_array_index(ol->obj_ids, i), 41);
|
||||
offset += 41;
|
||||
}
|
||||
|
||||
*buffer = buf;
|
||||
*len = 41 * ollen;
|
||||
}
|
||||
|
||||
gboolean
|
||||
object_list_insert (ObjectList *ol, const char *object_id)
|
||||
{
|
||||
if (g_hash_table_lookup (ol->obj_hash, object_id))
|
||||
return FALSE;
|
||||
char *id = g_strdup(object_id);
|
||||
g_hash_table_replace (ol->obj_hash, id, id);
|
||||
g_ptr_array_add (ol->obj_ids, id);
|
||||
return TRUE;
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#ifndef OBJECT_LIST_H
|
||||
#define OBJECT_LIST_H
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
typedef struct {
|
||||
GHashTable *obj_hash;
|
||||
GPtrArray *obj_ids;
|
||||
} ObjectList;
|
||||
|
||||
|
||||
ObjectList *
|
||||
object_list_new ();
|
||||
|
||||
void
|
||||
object_list_free (ObjectList *ol);
|
||||
|
||||
void
|
||||
object_list_serialize (ObjectList *ol, uint8_t **buffer, uint32_t *len);
|
||||
|
||||
/**
|
||||
* Add object to ObjectList.
|
||||
* Return FALSE if it is already in the list, TRUE otherwise.
|
||||
*/
|
||||
gboolean
|
||||
object_list_insert (ObjectList *ol, const char *object_id);
|
||||
|
||||
inline static gboolean
|
||||
object_list_exists (ObjectList *ol, const char *object_id)
|
||||
{
|
||||
return (g_hash_table_lookup(ol->obj_hash, object_id) != NULL);
|
||||
}
|
||||
|
||||
inline static int
|
||||
object_list_length (ObjectList *ol)
|
||||
{
|
||||
return ol->obj_ids->len;
|
||||
}
|
||||
|
||||
#endif
|
@ -1,52 +0,0 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#ifndef OBJECTTX_COMMON_H
|
||||
#define OBJECTTX_COMMON_H
|
||||
|
||||
#define SC_GET_OBJECT "301"
|
||||
#define SS_GET_OBJECT "Get Object"
|
||||
#define SC_OBJECT "302"
|
||||
#define SS_OBJECT "Object"
|
||||
#define SC_END "303"
|
||||
#define SS_END "END"
|
||||
#define SC_COMMIT_IDS "304"
|
||||
#define SS_COMMIT_IDS "Commit IDs"
|
||||
#define SC_ACK "305"
|
||||
#define SS_ACK "Ack"
|
||||
|
||||
#define SC_OBJ_SEG "306"
|
||||
#define SS_OBJ_SEG "Object Segment"
|
||||
#define SC_OBJ_SEG_END "307"
|
||||
#define SS_OBJ_SEG_END "Object Segment End"
|
||||
|
||||
#define SC_OBJ_LIST_SEG "308"
|
||||
#define SS_OBJ_LIST_SEG "Object List Segment"
|
||||
#define SC_OBJ_LIST_SEG_END "309"
|
||||
#define SS_OBJ_LIST_SEG_END "Object List Segment End"
|
||||
|
||||
#define SC_NOT_FOUND "401"
|
||||
#define SS_NOT_FOUND "Object not found"
|
||||
#define SC_BAD_OL "402"
|
||||
#define SS_BAD_OL "Bad Object List"
|
||||
#define SC_BAD_OBJECT "403"
|
||||
#define SS_BAD_OBJECT "Bad Object"
|
||||
|
||||
#define SC_ACCESS_DENIED "410"
|
||||
#define SS_ACCESS_DENIED "Access denied"
|
||||
|
||||
/* for fs transfer */
|
||||
#define SC_ROOT "304"
|
||||
#define SS_ROOT "FS Root"
|
||||
#define SC_ROOT_END "305"
|
||||
#define SS_ROOT_END "FS Root End"
|
||||
|
||||
/* max fs object segment size */
|
||||
#define MAX_OBJ_SEG_SIZE 64000
|
||||
|
||||
|
||||
typedef struct {
|
||||
char id[41];
|
||||
uint8_t object[0];
|
||||
} __attribute__((__packed__)) ObjectPack;
|
||||
|
||||
#endif
|
4060
common/rpc-service.c
4060
common/rpc-service.c
File diff suppressed because it is too large
Load Diff
1049
common/seaf-db.c
1049
common/seaf-db.c
File diff suppressed because it is too large
Load Diff
129
common/seaf-db.h
129
common/seaf-db.h
@ -1,129 +0,0 @@
|
||||
#ifndef SEAF_DB_H
|
||||
#define SEAF_DB_H
|
||||
|
||||
enum {
|
||||
SEAF_DB_TYPE_SQLITE,
|
||||
SEAF_DB_TYPE_MYSQL,
|
||||
SEAF_DB_TYPE_PGSQL,
|
||||
};
|
||||
|
||||
typedef struct SeafDB SeafDB;
|
||||
typedef struct SeafDBRow SeafDBRow;
|
||||
typedef struct SeafDBTrans SeafDBTrans;
|
||||
|
||||
typedef gboolean (*SeafDBRowFunc) (SeafDBRow *, void *);
|
||||
|
||||
SeafDB *
|
||||
seaf_db_new_mysql (const char *host,
|
||||
const char *port,
|
||||
const char *user,
|
||||
const char *passwd,
|
||||
const char *db,
|
||||
const char *unix_socket,
|
||||
gboolean use_ssl,
|
||||
const char *charset,
|
||||
int max_connections);
|
||||
|
||||
SeafDB *
|
||||
seaf_db_new_pgsql (const char *host,
|
||||
const char *user,
|
||||
const char *passwd,
|
||||
const char *db_name,
|
||||
const char *unix_socket);
|
||||
|
||||
SeafDB *
|
||||
seaf_db_new_sqlite (const char *db_path, int max_connections);
|
||||
|
||||
void
|
||||
seaf_db_free (SeafDB *db);
|
||||
|
||||
int
|
||||
seaf_db_type (SeafDB *db);
|
||||
|
||||
int
|
||||
seaf_db_query (SeafDB *db, const char *sql);
|
||||
|
||||
gboolean
|
||||
seaf_db_check_for_existence (SeafDB *db, const char *sql, gboolean *db_err);
|
||||
|
||||
int
|
||||
seaf_db_foreach_selected_row (SeafDB *db, const char *sql,
|
||||
SeafDBRowFunc callback, void *data);
|
||||
|
||||
const char *
|
||||
seaf_db_row_get_column_text (SeafDBRow *row, guint32 idx);
|
||||
|
||||
int
|
||||
seaf_db_row_get_column_int (SeafDBRow *row, guint32 idx);
|
||||
|
||||
gint64
|
||||
seaf_db_row_get_column_int64 (SeafDBRow *row, guint32 idx);
|
||||
|
||||
int
|
||||
seaf_db_get_int (SeafDB *db, const char *sql);
|
||||
|
||||
gint64
|
||||
seaf_db_get_int64 (SeafDB *db, const char *sql);
|
||||
|
||||
char *
|
||||
seaf_db_get_string (SeafDB *db, const char *sql);
|
||||
|
||||
/* Transaction related */
|
||||
|
||||
SeafDBTrans *
|
||||
seaf_db_begin_transaction (SeafDB *db);
|
||||
|
||||
void
|
||||
seaf_db_trans_close (SeafDBTrans *trans);
|
||||
|
||||
int
|
||||
seaf_db_commit (SeafDBTrans *trans);
|
||||
|
||||
int
|
||||
seaf_db_rollback (SeafDBTrans *trans);
|
||||
|
||||
int
|
||||
seaf_db_trans_query (SeafDBTrans *trans, const char *sql, int n, ...);
|
||||
|
||||
gboolean
|
||||
seaf_db_trans_check_for_existence (SeafDBTrans *trans,
|
||||
const char *sql,
|
||||
gboolean *db_err,
|
||||
int n, ...);
|
||||
|
||||
int
|
||||
seaf_db_trans_foreach_selected_row (SeafDBTrans *trans, const char *sql,
|
||||
SeafDBRowFunc callback, void *data,
|
||||
int n, ...);
|
||||
|
||||
/* Escape a string contant by doubling '\" characters.
|
||||
*/
|
||||
char *
|
||||
seaf_db_escape_string (SeafDB *db, const char *from);
|
||||
|
||||
gboolean
|
||||
pgsql_index_exists (SeafDB *db, const char *index_name);
|
||||
|
||||
/* Prepared Statements */
|
||||
|
||||
int
|
||||
seaf_db_statement_query (SeafDB *db, const char *sql, int n, ...);
|
||||
|
||||
gboolean
|
||||
seaf_db_statement_exists (SeafDB *db, const char *sql, gboolean *db_err, int n, ...);
|
||||
|
||||
int
|
||||
seaf_db_statement_foreach_row (SeafDB *db, const char *sql,
|
||||
SeafDBRowFunc callback, void *data,
|
||||
int n, ...);
|
||||
|
||||
int
|
||||
seaf_db_statement_get_int (SeafDB *db, const char *sql, int n, ...);
|
||||
|
||||
gint64
|
||||
seaf_db_statement_get_int64 (SeafDB *db, const char *sql, int n, ...);
|
||||
|
||||
char *
|
||||
seaf_db_statement_get_string (SeafDB *db, const char *sql, int n, ...);
|
||||
|
||||
#endif
|
@ -1,135 +0,0 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include "log.h"
|
||||
|
||||
#include "seafile-session.h"
|
||||
#include "fs-mgr.h"
|
||||
#include "seaf-tree-walk.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
void
|
||||
fill_tree_descriptor(const char *repo_id, int version,
|
||||
struct tree_desc *desc, const char *root_id)
|
||||
{
|
||||
SeafDir *dir;
|
||||
|
||||
if (!root_id) {
|
||||
desc->tree = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
dir = seaf_fs_manager_get_seafdir_sorted (seaf->fs_mgr,
|
||||
repo_id,
|
||||
version,
|
||||
root_id);
|
||||
if (!dir) {
|
||||
seaf_warning ("Failed to fill tree descriptor with %s.\n", root_id);
|
||||
desc->tree = NULL;
|
||||
}
|
||||
|
||||
desc->tree = dir;
|
||||
}
|
||||
|
||||
char *make_traverse_path(char *path, const struct traverse_info *info, const struct name_entry *n)
|
||||
{
|
||||
int len = n->pathlen;
|
||||
int pathlen = info->pathlen;
|
||||
|
||||
path[pathlen + len] = 0;
|
||||
for (;;) {
|
||||
memcpy(path + pathlen, n->path, len);
|
||||
if (!pathlen)
|
||||
break;
|
||||
path[--pathlen] = '/';
|
||||
n = &info->name;
|
||||
len = n->pathlen;
|
||||
info = info->prev;
|
||||
pathlen -= len;
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
int
|
||||
traverse_trees(int n, struct tree_desc *t, struct traverse_info *info)
|
||||
{
|
||||
struct name_entry *entries = g_new0 (struct name_entry, n);
|
||||
GList **ptrs = g_new0 (GList *, n);
|
||||
int i;
|
||||
SeafDirent *dent;
|
||||
char *first_name;
|
||||
gboolean done;
|
||||
unsigned long mask = 0, dirmask = 0;
|
||||
int error = 0, ret;
|
||||
|
||||
for (i = 0; i < n; ++i) {
|
||||
if (t[i].tree)
|
||||
ptrs[i] = t[i].tree->entries;
|
||||
else
|
||||
ptrs[i] = NULL;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
first_name = NULL;
|
||||
mask = dirmask = 0;
|
||||
memset (entries, 0, sizeof(entries[0])*n);
|
||||
done = TRUE;
|
||||
|
||||
/* Find the "largest" name, assuming dirents are sorted. */
|
||||
for (i = 0; i < n; ++i) {
|
||||
if (ptrs[i] != NULL) {
|
||||
done = FALSE;
|
||||
dent = ptrs[i]->data;
|
||||
if (!first_name)
|
||||
first_name = dent->name;
|
||||
else if (strcmp(dent->name, first_name) > 0)
|
||||
first_name = dent->name;
|
||||
}
|
||||
}
|
||||
|
||||
if (done)
|
||||
break;
|
||||
|
||||
/*
|
||||
* Setup name entries for all names that equals first_name
|
||||
*/
|
||||
for (i = 0; i < n; ++i) {
|
||||
if (ptrs[i] != NULL) {
|
||||
dent = ptrs[i]->data;
|
||||
if (strcmp(first_name, dent->name) == 0) {
|
||||
mask |= 1 << i;
|
||||
/* We treat empty dirs as a file. */
|
||||
if (S_ISDIR(dent->mode) &&
|
||||
memcmp (dent->id, EMPTY_SHA1, 40) != 0)
|
||||
dirmask |= 1 << i;
|
||||
|
||||
hex_to_rawdata (dent->id, entries[i].sha1, 20);
|
||||
entries[i].path = dent->name;
|
||||
entries[i].pathlen = dent->name_len;
|
||||
entries[i].mode = dent->mode;
|
||||
entries[i].mtime = dent->mtime;
|
||||
if (S_ISREG(dent->mode)) {
|
||||
entries[i].modifier = dent->modifier;
|
||||
}
|
||||
|
||||
ptrs[i] = ptrs[i]->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ret = info->fn (n, mask, dirmask, entries, info);
|
||||
if (ret < 0) {
|
||||
error = ret;
|
||||
}
|
||||
}
|
||||
|
||||
g_free (entries);
|
||||
g_free (ptrs);
|
||||
return error;
|
||||
}
|
@ -1,51 +0,0 @@
|
||||
#ifndef SEAF_TREE_WALK_H
|
||||
#define SEAF_TREE_WALK_H
|
||||
|
||||
#include "fs-mgr.h"
|
||||
|
||||
struct name_entry {
|
||||
unsigned char sha1[20];
|
||||
const char *path;
|
||||
int pathlen;
|
||||
unsigned int mode;
|
||||
char *modifier;
|
||||
guint64 mtime;
|
||||
};
|
||||
|
||||
struct tree_desc {
|
||||
SeafDir *tree;
|
||||
};
|
||||
|
||||
inline static void tree_desc_free (struct tree_desc *t)
|
||||
{
|
||||
if (t->tree)
|
||||
seaf_dir_free (t->tree);
|
||||
}
|
||||
|
||||
struct traverse_info;
|
||||
|
||||
typedef int (*traverse_callback_t)(int n, unsigned long mask, unsigned long dirmask, struct name_entry *entry, struct traverse_info *);
|
||||
|
||||
struct traverse_info {
|
||||
struct traverse_info *prev;
|
||||
struct name_entry name;
|
||||
int pathlen;
|
||||
|
||||
unsigned long conflicts;
|
||||
traverse_callback_t fn;
|
||||
void *data;
|
||||
int show_all_errors;
|
||||
};
|
||||
|
||||
void fill_tree_descriptor(const char *repo_id, int version,
|
||||
struct tree_desc *desc, const char *root_id);
|
||||
int traverse_trees(int n, struct tree_desc *t, struct traverse_info *info);
|
||||
char *make_traverse_path(char *path, const struct traverse_info *info, const struct name_entry *n);
|
||||
|
||||
static inline int traverse_path_len(const struct traverse_info *info, const struct name_entry *n)
|
||||
{
|
||||
return info->pathlen + n->pathlen;
|
||||
}
|
||||
|
||||
|
||||
#endif
|
@ -1,201 +0,0 @@
|
||||
#include "common.h"
|
||||
|
||||
#include "log.h"
|
||||
|
||||
#include "seafile-session.h"
|
||||
#include "seaf-utils.h"
|
||||
#include "seaf-db.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
char *
|
||||
seafile_session_get_tmp_file_path (SeafileSession *session,
|
||||
const char *basename,
|
||||
char path[])
|
||||
{
|
||||
int path_len;
|
||||
|
||||
path_len = strlen (session->tmp_file_dir);
|
||||
memcpy (path, session->tmp_file_dir, path_len + 1);
|
||||
path[path_len] = '/';
|
||||
strcpy (path + path_len + 1, basename);
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
#ifdef SEAFILE_SERVER
|
||||
|
||||
#define SQLITE_DB_NAME "seafile.db"
|
||||
|
||||
#define DEFAULT_MAX_CONNECTIONS 100
|
||||
|
||||
static int
|
||||
sqlite_db_start (SeafileSession *session)
|
||||
{
|
||||
char *db_path;
|
||||
int max_connections = 0;
|
||||
|
||||
max_connections = g_key_file_get_integer (session->config,
|
||||
"database", "max_connections",
|
||||
NULL);
|
||||
if (max_connections <= 0)
|
||||
max_connections = DEFAULT_MAX_CONNECTIONS;
|
||||
|
||||
db_path = g_build_filename (session->seaf_dir, SQLITE_DB_NAME, NULL);
|
||||
session->db = seaf_db_new_sqlite (db_path, max_connections);
|
||||
if (!session->db) {
|
||||
seaf_warning ("Failed to start sqlite db.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define MYSQL_DEFAULT_PORT "3306"
|
||||
|
||||
static int
|
||||
mysql_db_start (SeafileSession *session)
|
||||
{
|
||||
char *host, *port, *user, *passwd, *db, *unix_socket, *charset;
|
||||
gboolean use_ssl = FALSE;
|
||||
int max_connections = 0;
|
||||
GError *error = NULL;
|
||||
|
||||
host = g_key_file_get_string (session->config, "database", "host", &error);
|
||||
if (!host) {
|
||||
seaf_warning ("DB host not set in config.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
port = g_key_file_get_string (session->config, "database", "port", &error);
|
||||
if (!port) {
|
||||
port = g_strdup(MYSQL_DEFAULT_PORT);
|
||||
}
|
||||
|
||||
user = g_key_file_get_string (session->config, "database", "user", &error);
|
||||
if (!user) {
|
||||
seaf_warning ("DB user not set in config.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
passwd = g_key_file_get_string (session->config, "database", "password", &error);
|
||||
if (!passwd) {
|
||||
seaf_warning ("DB passwd not set in config.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
db = g_key_file_get_string (session->config, "database", "db_name", &error);
|
||||
if (!db) {
|
||||
seaf_warning ("DB name not set in config.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
unix_socket = g_key_file_get_string (session->config,
|
||||
"database", "unix_socket", NULL);
|
||||
|
||||
use_ssl = g_key_file_get_boolean (session->config,
|
||||
"database", "use_ssl", NULL);
|
||||
|
||||
charset = g_key_file_get_string (session->config,
|
||||
"database", "connection_charset", NULL);
|
||||
|
||||
max_connections = g_key_file_get_integer (session->config,
|
||||
"database", "max_connections",
|
||||
NULL);
|
||||
if (max_connections <= 0)
|
||||
max_connections = DEFAULT_MAX_CONNECTIONS;
|
||||
|
||||
session->db = seaf_db_new_mysql (host, port, user, passwd, db, unix_socket, use_ssl, charset, max_connections);
|
||||
if (!session->db) {
|
||||
seaf_warning ("Failed to start mysql db.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
g_free (host);
|
||||
g_free (port);
|
||||
g_free (user);
|
||||
g_free (passwd);
|
||||
g_free (db);
|
||||
g_free (unix_socket);
|
||||
g_free (charset);
|
||||
if (error)
|
||||
g_clear_error (&error);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
pgsql_db_start (SeafileSession *session)
|
||||
{
|
||||
char *host, *user, *passwd, *db, *unix_socket;
|
||||
GError *error = NULL;
|
||||
|
||||
host = g_key_file_get_string (session->config, "database", "host", &error);
|
||||
if (!host) {
|
||||
seaf_warning ("DB host not set in config.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
user = g_key_file_get_string (session->config, "database", "user", &error);
|
||||
if (!user) {
|
||||
seaf_warning ("DB user not set in config.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
passwd = g_key_file_get_string (session->config, "database", "password", &error);
|
||||
if (!passwd) {
|
||||
seaf_warning ("DB passwd not set in config.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
db = g_key_file_get_string (session->config, "database", "db_name", &error);
|
||||
if (!db) {
|
||||
seaf_warning ("DB name not set in config.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
unix_socket = g_key_file_get_string (session->config,
|
||||
"database", "unix_socket", &error);
|
||||
|
||||
session->db = seaf_db_new_pgsql (host, user, passwd, db, unix_socket);
|
||||
if (!session->db) {
|
||||
seaf_warning ("Failed to start pgsql db.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
g_free (host);
|
||||
g_free (user);
|
||||
g_free (passwd);
|
||||
g_free (db);
|
||||
g_free (unix_socket);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
load_database_config (SeafileSession *session)
|
||||
{
|
||||
char *type;
|
||||
GError *error = NULL;
|
||||
int ret = 0;
|
||||
|
||||
type = g_key_file_get_string (session->config, "database", "type", &error);
|
||||
/* Default to use sqlite if not set. */
|
||||
if (!type || strcasecmp (type, "sqlite") == 0) {
|
||||
ret = sqlite_db_start (session);
|
||||
} else if (strcasecmp (type, "mysql") == 0) {
|
||||
ret = mysql_db_start (session);
|
||||
} else if (strcasecmp (type, "pgsql") == 0) {
|
||||
ret = pgsql_db_start (session);
|
||||
} else {
|
||||
seaf_warning ("Unsupported db type %s.\n", type);
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
g_free (type);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif
|
@ -1,17 +0,0 @@
|
||||
#ifndef SEAF_UTILS_H
|
||||
#define SEAF_UTILS_H
|
||||
|
||||
struct _SeafileSession;
|
||||
|
||||
|
||||
char *
|
||||
seafile_session_get_tmp_file_path (struct _SeafileSession *session,
|
||||
const char *basename,
|
||||
char path[]);
|
||||
|
||||
#ifdef SEAFILE_SERVER
|
||||
int
|
||||
load_database_config (struct _SeafileSession *session);
|
||||
#endif
|
||||
|
||||
#endif
|
@ -1,20 +0,0 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#ifndef SYNC_REPO_COMMON
|
||||
#define SYNC_REPO_COMMON
|
||||
|
||||
#define SC_COMMIT_ID "300"
|
||||
#define SS_COMMIT_ID "Commit ID"
|
||||
#define SC_NO_REPO "301"
|
||||
#define SS_NO_REPO "No such repo"
|
||||
#define SC_NO_BRANCH "302"
|
||||
#define SS_NO_BRANCH "No such branch"
|
||||
#define SC_NO_DSYNC "303"
|
||||
#define SS_NO_DSYNC "Not double sync"
|
||||
#define SC_REPO_CORRUPT "304"
|
||||
#define SS_REPO_CORRUPT "Repo corrupted"
|
||||
|
||||
#define SC_SERVER_ERROR "401"
|
||||
#define SS_SERVER_ERROR "Internal server error"
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
@ -1,84 +0,0 @@
|
||||
#ifndef UNPACK_TREES_H
|
||||
#define UNPACK_TREES_H
|
||||
|
||||
#include "common.h"
|
||||
#include "utils.h"
|
||||
#include "seaf-tree-walk.h"
|
||||
#include "index/index.h"
|
||||
#include "seafile-crypt.h"
|
||||
|
||||
#define MAX_UNPACK_TREES 8
|
||||
|
||||
struct unpack_trees_options;
|
||||
|
||||
typedef int (*merge_fn_t)(struct cache_entry **src,
|
||||
struct unpack_trees_options *options);
|
||||
|
||||
enum unpack_trees_error_types {
|
||||
ERROR_WOULD_OVERWRITE = 0,
|
||||
ERROR_NOT_UPTODATE_FILE,
|
||||
ERROR_NOT_UPTODATE_DIR,
|
||||
ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN,
|
||||
ERROR_WOULD_LOSE_UNTRACKED_REMOVED,
|
||||
NB_UNPACK_TREES_ERROR_TYPES
|
||||
};
|
||||
|
||||
struct unpack_trees_options {
|
||||
unsigned int reset,
|
||||
merge,
|
||||
update,
|
||||
index_only,
|
||||
nontrivial_merge,
|
||||
trivial_merges_only,
|
||||
verbose_update,
|
||||
aggressive,
|
||||
skip_unmerged,
|
||||
initial_checkout,
|
||||
diff_index_cached,
|
||||
debug_unpack,
|
||||
skip_sparse_checkout,
|
||||
gently,
|
||||
show_all_errors;
|
||||
char repo_id[37];
|
||||
int version;
|
||||
const char *prefix;
|
||||
const char *base;
|
||||
int cache_bottom;
|
||||
merge_fn_t fn;
|
||||
const char *msgs[NB_UNPACK_TREES_ERROR_TYPES];
|
||||
/*
|
||||
* Store error messages in an array, each case
|
||||
* corresponding to a error message type
|
||||
*/
|
||||
GList *unpack_rejects[NB_UNPACK_TREES_ERROR_TYPES];
|
||||
|
||||
int head_idx;
|
||||
int merge_size;
|
||||
|
||||
struct cache_entry *df_conflict_entry;
|
||||
void *unpack_data;
|
||||
|
||||
struct index_state *dst_index;
|
||||
struct index_state *src_index;
|
||||
struct index_state result;
|
||||
|
||||
SeafileCrypt *crypt;
|
||||
};
|
||||
|
||||
extern int unpack_trees(unsigned n, struct tree_desc *t,
|
||||
struct unpack_trees_options *options);
|
||||
|
||||
enum {
|
||||
OPR_CHECKOUT,
|
||||
OPR_MERGE,
|
||||
N_OPR_TYPES,
|
||||
};
|
||||
|
||||
gboolean
|
||||
get_unpack_trees_error_msgs(struct unpack_trees_options *o, GString *msgbuf, int opr_type);
|
||||
|
||||
int threeway_merge(struct cache_entry **stages, struct unpack_trees_options *o);
|
||||
int twoway_merge(struct cache_entry **src, struct unpack_trees_options *o);
|
||||
int oneway_merge(struct cache_entry **src, struct unpack_trees_options *o);
|
||||
|
||||
#endif
|
@ -151,7 +151,6 @@ AC_SUBST(LIB_ICONV)
|
||||
|
||||
LIBEVENT_REQUIRED=2.0
|
||||
GLIB_REQUIRED=2.16.0
|
||||
CCNET_REQUIRED=0.9.3
|
||||
SEARPC_REQUIRED=1.0
|
||||
JANSSON_REQUIRED=2.2.1
|
||||
CURL_REQUIRED=7.17
|
||||
@ -166,10 +165,6 @@ PKG_CHECK_MODULES(GOBJECT, [gobject-2.0 >= $GLIB_REQUIRED])
|
||||
AC_SUBST(GOBJECT_CFLAGS)
|
||||
AC_SUBST(GOBJECT_LIBS)
|
||||
|
||||
PKG_CHECK_MODULES(CCNET, [libccnet >= $CCNET_REQUIRED])
|
||||
AC_SUBST(CCNET_CFLAGS)
|
||||
AC_SUBST(CCNET_LIBS)
|
||||
|
||||
PKG_CHECK_MODULES(SEARPC, [libsearpc >= $SEARPC_REQUIRED])
|
||||
AC_SUBST(SEARPC_CFLAGS)
|
||||
AC_SUBST(SEARPC_LIBS)
|
||||
|
@ -6,7 +6,6 @@ AM_CFLAGS = -DPKGDATADIR=\"$(pkgdatadir)\" \
|
||||
-I$(top_srcdir)/lib \
|
||||
-I$(top_builddir)/lib \
|
||||
-I$(top_srcdir)/common \
|
||||
@CCNET_CFLAGS@ \
|
||||
@SEARPC_CFLAGS@ \
|
||||
@GLIB2_CFLAGS@ \
|
||||
@MSVC_CFLAGS@ \
|
||||
@ -17,45 +16,22 @@ AM_CFLAGS = -DPKGDATADIR=\"$(pkgdatadir)\" \
|
||||
|
||||
bin_PROGRAMS = seaf-daemon
|
||||
|
||||
proc_headers = $(addprefix processors/, \
|
||||
check-tx-v3-proc.h \
|
||||
sendfs-proc.h \
|
||||
getfs-proc.h \
|
||||
sendbranch-proc.h \
|
||||
getcs-proc.h \
|
||||
sync-repo-proc.h \
|
||||
getcommit-v2-proc.h \
|
||||
sendcommit-v3-proc.h \
|
||||
sendcommit-v3-new-proc.h \
|
||||
getcs-v2-proc.h \
|
||||
checkbl-proc.h \
|
||||
getcommit-v3-proc.h \
|
||||
checkff-proc.h \
|
||||
getca-proc.h \
|
||||
check-protocol-proc.h \
|
||||
sendcommit-v4-proc.h \
|
||||
sendfs-v2-proc.h \
|
||||
getfs-v2-proc.h)
|
||||
|
||||
proc_headers += ../common/processors/objecttx-common.h
|
||||
|
||||
noinst_HEADERS = \
|
||||
job-mgr.h \
|
||||
timer.h \
|
||||
cevent.h \
|
||||
repo-mgr.h \
|
||||
transfer-mgr.h \
|
||||
status.h sync-mgr.h \
|
||||
sync-mgr.h \
|
||||
wt-monitor.h \
|
||||
merge.h merge-recursive.h vc-utils.h seafile-session.h \
|
||||
vc-utils.h seafile-session.h \
|
||||
clone-mgr.h \
|
||||
wt-monitor-structs.h \
|
||||
../common/sync-repo-common.h \
|
||||
block-tx-client.h \
|
||||
seafile-config.h \
|
||||
http-tx-mgr.h \
|
||||
sync-status-tree.h \
|
||||
filelock-mgr.h \
|
||||
set-perm.h \
|
||||
change-set.h \
|
||||
$(proc_headers)
|
||||
change-set.h
|
||||
|
||||
if LINUX
|
||||
wt_monitor_src = wt-monitor.c wt-monitor-linux.c wt-monitor-structs.c
|
||||
@ -70,20 +46,18 @@ wt_monitor_src = wt-monitor.c wt-monitor-macos.c wt-monitor-structs.c
|
||||
endif
|
||||
|
||||
common_src = \
|
||||
job-mgr.c timer.c cevent.c \
|
||||
http-tx-mgr.c \
|
||||
transfer-mgr.c \
|
||||
../common/unpack-trees.c ../common/seaf-tree-walk.c \
|
||||
merge.c merge-recursive.c vc-utils.c \
|
||||
status.c sync-mgr.c seafile-session.c \
|
||||
vc-utils.c \
|
||||
sync-mgr.c seafile-session.c \
|
||||
../common/seafile-crypt.c ../common/diff-simple.c $(wt_monitor_src) \
|
||||
clone-mgr.c \
|
||||
seafile-config.c \
|
||||
../common/branch-mgr.c ../common/fs-mgr.c \
|
||||
repo-mgr.c ../common/commit-mgr.c \
|
||||
../common/log.c ../common/object-list.c \
|
||||
../common/log.c \
|
||||
../common/rpc-service.c \
|
||||
../common/vc-common.c \
|
||||
../common/seaf-utils.c \
|
||||
../common/obj-store.c \
|
||||
../common/obj-backend-fs.c \
|
||||
../common/block-mgr.c \
|
||||
@ -91,30 +65,10 @@ common_src = \
|
||||
../common/block-backend-fs.c \
|
||||
../common/mq-mgr.c \
|
||||
../common/curl-init.c \
|
||||
block-tx-client.c \
|
||||
../common/block-tx-utils.c \
|
||||
sync-status-tree.c \
|
||||
filelock-mgr.c \
|
||||
set-perm.c \
|
||||
change-set.c \
|
||||
processors/check-tx-v3-proc.c \
|
||||
processors/sendfs-proc.c \
|
||||
processors/getfs-proc.c \
|
||||
processors/sendbranch-proc.c \
|
||||
processors/getcs-proc.c \
|
||||
processors/sync-repo-proc.c \
|
||||
processors/getcommit-v2-proc.c \
|
||||
processors/sendcommit-v3-proc.c \
|
||||
processors/sendcommit-v3-new-proc.c \
|
||||
processors/getcs-v2-proc.c \
|
||||
processors/checkbl-proc.c \
|
||||
processors/getcommit-v3-proc.c \
|
||||
processors/checkff-proc.c \
|
||||
processors/getca-proc.c \
|
||||
processors/check-protocol-proc.c \
|
||||
processors/sendcommit-v4-proc.c \
|
||||
processors/sendfs-v2-proc.c \
|
||||
processors/getfs-v2-proc.c
|
||||
change-set.c
|
||||
|
||||
|
||||
seaf_daemon_SOURCES = seaf-daemon.c $(common_src)
|
||||
@ -124,6 +78,6 @@ seaf_daemon_LDADD = $(top_builddir)/lib/libseafile_common.la \
|
||||
@LIB_RT@ @LIB_UUID@ -lsqlite3 @LIBEVENT_LIBS@ \
|
||||
$(top_builddir)/common/cdc/libcdc.la \
|
||||
$(top_builddir)/common/index/libindex.la @LIB_WS32@ @LIB_CRYPT32@ \
|
||||
@SEARPC_LIBS@ @CCNET_LIBS@ @JANSSON_LIBS@ @LIB_MAC@ @ZLIB_LIBS@ @CURL_LIBS@ @BPWRAPPER_LIBS@
|
||||
@SEARPC_LIBS@ @JANSSON_LIBS@ @LIB_MAC@ @ZLIB_LIBS@ @CURL_LIBS@ @BPWRAPPER_LIBS@
|
||||
|
||||
seaf_daemon_LDFLAGS = @CONSOLE@
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,38 +0,0 @@
|
||||
#ifndef BLOCK_TX_CLIENT_H
|
||||
#define BLOCK_TX_CLIENT_H
|
||||
|
||||
#include "transfer-mgr.h"
|
||||
|
||||
typedef void (*BlockTxClientDoneCB) (BlockTxInfo *);
|
||||
|
||||
/*
|
||||
* There are two modes to use block-tx-client:
|
||||
*
|
||||
* 1. In upload, the client is set to one-time mode.
|
||||
* After all blocks are uploaded, the client done callback is called.
|
||||
*
|
||||
* 2. In download, the client is set to interactive mode.
|
||||
* The block tx client first has to connect to the server and do authentication.
|
||||
* After authentication is done, block-tx-client writes a READY reply to done_pipe.
|
||||
* Transfer manager waits on the done_pipe for the READY reply.
|
||||
* Transfer manager then initiates multiple batches of blocks for download.
|
||||
* After each batch is downloaded, the block client writes to info->done_pipe
|
||||
* to notify transfer manager.
|
||||
* After all blocks are downloaded, transfer manager send a END command to
|
||||
* block client. The block client exits and returns an ENDED response code.
|
||||
*/
|
||||
|
||||
int
|
||||
block_tx_client_start (BlockTxInfo *info, BlockTxClientDoneCB cb);
|
||||
|
||||
enum {
|
||||
BLOCK_CLIENT_CMD_TRANSFER = 0,
|
||||
BLOCK_CLIENT_CMD_CANCEL,
|
||||
BLOCK_CLIENT_CMD_END,
|
||||
BLOCK_CLIENT_CMD_RESTART,
|
||||
};
|
||||
|
||||
void
|
||||
block_tx_client_run_command (BlockTxInfo *info, int command);
|
||||
|
||||
#endif
|
106
daemon/cevent.c
Normal file
106
daemon/cevent.c
Normal file
@ -0,0 +1,106 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#include "include.h"
|
||||
#include "cevent.h"
|
||||
|
||||
#include "seafile-session.h"
|
||||
|
||||
#define CEVENT_SIZE (sizeof(CEvent))
|
||||
|
||||
typedef struct Handler {
|
||||
cevent_handler handler;
|
||||
void *handler_data;
|
||||
} Handler;
|
||||
|
||||
CEventManager* cevent_manager_new ()
|
||||
{
|
||||
CEventManager *manager;
|
||||
|
||||
manager = g_new0 (CEventManager, 1);
|
||||
pthread_mutex_init (&manager->mutex, NULL);
|
||||
manager->handler_table = g_hash_table_new_full (g_direct_hash,
|
||||
g_direct_equal, NULL, g_free);
|
||||
|
||||
return manager;
|
||||
}
|
||||
|
||||
void pipe_callback (evutil_socket_t fd, short event, void *vmgr)
|
||||
{
|
||||
CEventManager *manager = (CEventManager *) vmgr;
|
||||
CEvent *cevent;
|
||||
char buf[CEVENT_SIZE];
|
||||
|
||||
if (seaf_pipe_readn(fd, buf, CEVENT_SIZE) != CEVENT_SIZE) {
|
||||
return;
|
||||
}
|
||||
|
||||
cevent = (CEvent *)buf;
|
||||
Handler *h = g_hash_table_lookup (manager->handler_table,
|
||||
(gconstpointer)(long)cevent->id);
|
||||
if (h == NULL) {
|
||||
g_warning ("no handler for event type %d\n", cevent->id);
|
||||
return;
|
||||
}
|
||||
|
||||
h->handler(cevent, h->handler_data);
|
||||
}
|
||||
|
||||
int cevent_manager_start (CEventManager *manager)
|
||||
{
|
||||
if (seaf_pipe(manager->pipefd) < 0) {
|
||||
g_warning ("pipe error: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
manager->event = event_new (seaf->ev_base, manager->pipefd[0],
|
||||
EV_READ | EV_PERSIST, pipe_callback, manager);
|
||||
event_add (manager->event, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t cevent_manager_register (CEventManager *manager,
|
||||
cevent_handler handler, void *handler_data)
|
||||
{
|
||||
uint32_t id;
|
||||
Handler *h;
|
||||
|
||||
h = g_new0(Handler, 1);
|
||||
h->handler = handler;
|
||||
h->handler_data = handler_data;
|
||||
|
||||
/* Since we're using 32-bit int for id, it may wrap around to 0.
|
||||
* If some caller persistently use one id, it's handler may be
|
||||
* overwritten by others.
|
||||
*/
|
||||
do {
|
||||
id = manager->next_id++;
|
||||
} while (g_hash_table_lookup (manager->handler_table, (gpointer)(long)id));
|
||||
|
||||
g_hash_table_insert (manager->handler_table, (gpointer)(long)id, h);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
void cevent_manager_unregister (CEventManager *manager, uint32_t id)
|
||||
{
|
||||
g_hash_table_remove (manager->handler_table, (gpointer)(long)id);
|
||||
}
|
||||
|
||||
void
|
||||
cevent_manager_add_event (CEventManager *manager, uint32_t id,
|
||||
void *data)
|
||||
{
|
||||
pthread_mutex_lock (&manager->mutex);
|
||||
|
||||
struct CEvent cevent;
|
||||
char *buf = (char *) &cevent;
|
||||
|
||||
cevent.id = id;
|
||||
cevent.data = data;
|
||||
if (seaf_pipe_writen(manager->pipefd[1], buf, CEVENT_SIZE) != CEVENT_SIZE) {
|
||||
g_warning ("add event error\n");
|
||||
}
|
||||
|
||||
pthread_mutex_unlock (&manager->mutex);
|
||||
}
|
56
daemon/cevent.h
Normal file
56
daemon/cevent.h
Normal file
@ -0,0 +1,56 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
/*
|
||||
* CEvent is used for send message from a work thread to main thread.
|
||||
*/
|
||||
#ifndef CEVENT_H
|
||||
#define CEVENT_H
|
||||
|
||||
#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
|
||||
#include <event2/event.h>
|
||||
#include <event2/event_compat.h>
|
||||
#include <event2/event_struct.h>
|
||||
#else
|
||||
#include <event.h>
|
||||
#endif
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
typedef struct CEvent CEvent;
|
||||
|
||||
typedef void (*cevent_handler) (CEvent *event, void *handler_data);
|
||||
|
||||
struct CEvent {
|
||||
uint32_t id;
|
||||
void *data;
|
||||
};
|
||||
|
||||
|
||||
typedef struct CEventManager CEventManager;
|
||||
|
||||
struct CEventManager {
|
||||
seaf_pipe_t pipefd[2];
|
||||
struct event *event;
|
||||
GHashTable *handler_table;
|
||||
uint32_t next_id;
|
||||
|
||||
pthread_mutex_t mutex;
|
||||
};
|
||||
|
||||
CEventManager* cevent_manager_new ();
|
||||
|
||||
int cevent_manager_start (CEventManager *manager);
|
||||
|
||||
uint32_t cevent_manager_register (CEventManager *manager,
|
||||
cevent_handler handler, void *handler_data);
|
||||
|
||||
void cevent_manager_unregister (CEventManager *manager, uint32_t id);
|
||||
|
||||
void cevent_manager_add_event (CEventManager *manager, uint32_t id,
|
||||
void *event_data);
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
@ -70,10 +70,7 @@ struct _CloneTask {
|
||||
char *effective_url;
|
||||
gboolean use_fileserver_port;
|
||||
int http_protocol_version;
|
||||
gboolean http_sync;
|
||||
char server_head_id[41];
|
||||
|
||||
gboolean server_side_merge;
|
||||
};
|
||||
|
||||
const char *
|
||||
@ -86,7 +83,7 @@ struct _SeafCloneManager {
|
||||
struct _SeafileSession *seaf;
|
||||
sqlite3 *db;
|
||||
GHashTable *tasks;
|
||||
struct CcnetTimer *check_timer;
|
||||
struct SeafTimer *check_timer;
|
||||
};
|
||||
|
||||
SeafCloneManager *
|
||||
|
@ -21,8 +21,6 @@
|
||||
#include <openssl/ssl.h>
|
||||
#endif
|
||||
|
||||
#include <ccnet/ccnet-client.h>
|
||||
|
||||
#include "seafile-config.h"
|
||||
|
||||
#include "seafile-session.h"
|
||||
@ -35,6 +33,8 @@
|
||||
#define DEBUG_FLAG SEAFILE_DEBUG_TRANSFER
|
||||
#include "log.h"
|
||||
|
||||
#include "timer.h"
|
||||
|
||||
#define HTTP_OK 200
|
||||
#define HTTP_BAD_REQUEST 400
|
||||
#define HTTP_FORBIDDEN 403
|
||||
@ -90,7 +90,7 @@ struct _HttpTxPriv {
|
||||
GHashTable *connection_pools; /* host -> connection pool */
|
||||
pthread_mutex_t pools_lock;
|
||||
|
||||
CcnetTimer *reset_bytes_timer;
|
||||
SeafTimer *reset_bytes_timer;
|
||||
|
||||
char *ca_bundle_path;
|
||||
|
||||
@ -367,9 +367,9 @@ http_tx_manager_start (HttpTxManager *mgr)
|
||||
|
||||
/* TODO: add a timer to clean up unused Http connections. */
|
||||
|
||||
mgr->priv->reset_bytes_timer = ccnet_timer_new (reset_bytes,
|
||||
mgr,
|
||||
RESET_BYTES_INTERVAL_MSEC);
|
||||
mgr->priv->reset_bytes_timer = seaf_timer_new (reset_bytes,
|
||||
mgr,
|
||||
RESET_BYTES_INTERVAL_MSEC);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1299,10 +1299,10 @@ http_tx_manager_check_protocol_version (HttpTxManager *manager,
|
||||
data->callback = callback;
|
||||
data->user_data = user_data;
|
||||
|
||||
int ret = ccnet_job_manager_schedule_job (seaf->job_mgr,
|
||||
check_protocol_version_thread,
|
||||
check_protocol_version_done,
|
||||
data);
|
||||
int ret = seaf_job_manager_schedule_job (seaf->job_mgr,
|
||||
check_protocol_version_thread,
|
||||
check_protocol_version_done,
|
||||
data);
|
||||
if (ret < 0) {
|
||||
g_free (data->host);
|
||||
g_free (data);
|
||||
@ -1463,10 +1463,10 @@ http_tx_manager_check_head_commit (HttpTxManager *manager,
|
||||
data->user_data = user_data;
|
||||
data->use_fileserver_port = use_fileserver_port;
|
||||
|
||||
if (ccnet_job_manager_schedule_job (seaf->job_mgr,
|
||||
check_head_commit_thread,
|
||||
check_head_commit_done,
|
||||
data) < 0) {
|
||||
if (seaf_job_manager_schedule_job (seaf->job_mgr,
|
||||
check_head_commit_thread,
|
||||
check_head_commit_done,
|
||||
data) < 0) {
|
||||
g_free (data->host);
|
||||
g_free (data->token);
|
||||
g_free (data);
|
||||
@ -1817,10 +1817,10 @@ http_tx_manager_get_folder_perms (HttpTxManager *manager,
|
||||
data->user_data = user_data;
|
||||
data->use_fileserver_port = use_fileserver_port;
|
||||
|
||||
if (ccnet_job_manager_schedule_job (seaf->job_mgr,
|
||||
get_folder_perms_thread,
|
||||
get_folder_perms_done,
|
||||
data) < 0) {
|
||||
if (seaf_job_manager_schedule_job (seaf->job_mgr,
|
||||
get_folder_perms_thread,
|
||||
get_folder_perms_done,
|
||||
data) < 0) {
|
||||
g_free (data->host);
|
||||
g_free (data);
|
||||
return -1;
|
||||
@ -2093,10 +2093,10 @@ http_tx_manager_get_locked_files (HttpTxManager *manager,
|
||||
data->user_data = user_data;
|
||||
data->use_fileserver_port = use_fileserver_port;
|
||||
|
||||
if (ccnet_job_manager_schedule_job (seaf->job_mgr,
|
||||
get_locked_files_thread,
|
||||
get_locked_files_done,
|
||||
data) < 0) {
|
||||
if (seaf_job_manager_schedule_job (seaf->job_mgr,
|
||||
get_locked_files_thread,
|
||||
get_locked_files_done,
|
||||
data) < 0) {
|
||||
g_free (data->host);
|
||||
g_free (data);
|
||||
return -1;
|
||||
@ -2377,7 +2377,7 @@ check_permission (HttpTxTask *task, Connection *conn)
|
||||
url = g_strdup_printf ("%s/%srepo/%s/permission-check/?op=%s"
|
||||
"&client_id=%s&client_name=%s",
|
||||
task->host, url_prefix, task->repo_id, type,
|
||||
seaf->session->base.id, client_name);
|
||||
seaf->client_id, client_name);
|
||||
g_free (client_name);
|
||||
} else {
|
||||
url = g_strdup_printf ("%s/%srepo/%s/permission-check/?op=%s",
|
||||
@ -2450,10 +2450,10 @@ http_tx_manager_add_upload (HttpTxManager *manager,
|
||||
g_strdup(repo_id),
|
||||
task);
|
||||
|
||||
if (ccnet_job_manager_schedule_job (seaf->job_mgr,
|
||||
http_upload_thread,
|
||||
http_upload_done,
|
||||
task) < 0) {
|
||||
if (seaf_job_manager_schedule_job (seaf->job_mgr,
|
||||
http_upload_thread,
|
||||
http_upload_done,
|
||||
task) < 0) {
|
||||
g_hash_table_remove (manager->priv->upload_tasks, repo_id);
|
||||
return -1;
|
||||
}
|
||||
@ -3826,10 +3826,10 @@ http_tx_manager_add_download (HttpTxManager *manager,
|
||||
NULL);
|
||||
task->repo_name = g_strdup(repo_name);
|
||||
|
||||
if (ccnet_job_manager_schedule_job (seaf->job_mgr,
|
||||
http_download_thread,
|
||||
http_download_done,
|
||||
task) < 0) {
|
||||
if (seaf_job_manager_schedule_job (seaf->job_mgr,
|
||||
http_download_thread,
|
||||
http_download_done,
|
||||
task) < 0) {
|
||||
g_hash_table_remove (manager->priv->download_tasks, repo_id);
|
||||
return -1;
|
||||
}
|
||||
@ -4518,7 +4518,7 @@ http_download_thread (void *vdata)
|
||||
REPO_PROP_DOWNLOAD_HEAD,
|
||||
task->head);
|
||||
|
||||
int rc = seaf_repo_fetch_and_checkout (NULL, task, TRUE, task->head);
|
||||
int rc = seaf_repo_fetch_and_checkout (task, task->head);
|
||||
switch (rc) {
|
||||
case FETCH_CHECKOUT_SUCCESS:
|
||||
break;
|
||||
@ -4644,7 +4644,7 @@ http_tx_manager_cancel_task (HttpTxManager *manager,
|
||||
}
|
||||
|
||||
if (task->runtime_state == HTTP_TASK_RT_STATE_INIT) {
|
||||
transition_state (task, HTTP_TASK_STATE_CANCELED, TASK_RT_STATE_FINISHED);
|
||||
transition_state (task, HTTP_TASK_STATE_CANCELED, HTTP_TASK_RT_STATE_FINISHED);
|
||||
return;
|
||||
}
|
||||
|
||||
|
149
daemon/job-mgr.c
Normal file
149
daemon/job-mgr.c
Normal file
@ -0,0 +1,149 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
|
||||
#include <event2/event.h>
|
||||
#include <event2/event_compat.h>
|
||||
#else
|
||||
#include <event.h>
|
||||
#endif
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "utils.h"
|
||||
#include "log.h"
|
||||
|
||||
#include "seafile-session.h"
|
||||
#include "job-mgr.h"
|
||||
|
||||
struct _SeafJobManager {
|
||||
SeafileSession *session;
|
||||
GThreadPool *thread_pool;
|
||||
int next_job_id;
|
||||
};
|
||||
|
||||
struct _SeafJob {
|
||||
SeafJobManager *manager;
|
||||
|
||||
int id;
|
||||
seaf_pipe_t pipefd[2];
|
||||
|
||||
JobThreadFunc thread_func;
|
||||
JobDoneCallback done_func; /* called when the thread is done */
|
||||
void *data;
|
||||
|
||||
/* the done callback should only access this field */
|
||||
void *result;
|
||||
};
|
||||
typedef struct _SeafJob SeafJob;
|
||||
|
||||
SeafJob *
|
||||
seaf_job_new ()
|
||||
{
|
||||
SeafJob *job;
|
||||
|
||||
job = g_new0 (SeafJob, 1);
|
||||
return job;
|
||||
}
|
||||
|
||||
void
|
||||
seaf_job_free (SeafJob *job)
|
||||
{
|
||||
g_free (job);
|
||||
}
|
||||
|
||||
static void
|
||||
job_thread_wrapper (void *vdata, void *unused)
|
||||
{
|
||||
SeafJob *job = vdata;
|
||||
|
||||
job->result = job->thread_func (job->data);
|
||||
if (seaf_pipe_writen (job->pipefd[1], "a", 1) != 1) {
|
||||
seaf_warning ("[Job Manager] write to pipe error: %s\n", strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
job_done_cb (evutil_socket_t fd, short event, void *vdata)
|
||||
{
|
||||
SeafJob *job = vdata;
|
||||
char buf[1];
|
||||
|
||||
if (seaf_pipe_readn (job->pipefd[0], buf, 1) != 1) {
|
||||
seaf_warning ("[Job Manager] read pipe error: %s\n", strerror(errno));
|
||||
}
|
||||
seaf_pipe_close (job->pipefd[0]);
|
||||
seaf_pipe_close (job->pipefd[1]);
|
||||
if (job->done_func) {
|
||||
job->done_func (job->result);
|
||||
}
|
||||
|
||||
seaf_job_free (job);
|
||||
}
|
||||
|
||||
int
|
||||
job_thread_create (SeafJob *job)
|
||||
{
|
||||
SeafileSession *session = job->manager->session;
|
||||
|
||||
if (seaf_pipe (job->pipefd) < 0) {
|
||||
seaf_warning ("[Job Manager] pipe error: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
g_thread_pool_push (job->manager->thread_pool, job, NULL);
|
||||
|
||||
event_base_once (session->ev_base, job->pipefd[0], EV_READ, job_done_cb, job, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
SeafJobManager *
|
||||
seaf_job_manager_new (SeafileSession *session, int max_threads)
|
||||
{
|
||||
SeafJobManager *mgr;
|
||||
|
||||
mgr = g_new0 (SeafJobManager, 1);
|
||||
mgr->session = session;
|
||||
mgr->thread_pool = g_thread_pool_new (job_thread_wrapper,
|
||||
NULL,
|
||||
max_threads,
|
||||
FALSE,
|
||||
NULL);
|
||||
|
||||
return mgr;
|
||||
}
|
||||
|
||||
void
|
||||
seaf_job_manager_free (SeafJobManager *mgr)
|
||||
{
|
||||
g_thread_pool_free (mgr->thread_pool, TRUE, FALSE);
|
||||
g_free (mgr);
|
||||
}
|
||||
|
||||
int
|
||||
seaf_job_manager_schedule_job (SeafJobManager *mgr,
|
||||
JobThreadFunc func,
|
||||
JobDoneCallback done_func,
|
||||
void *data)
|
||||
{
|
||||
SeafJob *job = seaf_job_new ();
|
||||
job->id = mgr->next_job_id++;
|
||||
job->manager = mgr;
|
||||
job->thread_func = func;
|
||||
job->done_func = done_func;
|
||||
job->data = data;
|
||||
|
||||
if (job_thread_create (job) < 0) {
|
||||
seaf_job_free (job);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
36
daemon/job-mgr.h
Normal file
36
daemon/job-mgr.h
Normal file
@ -0,0 +1,36 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
/**
|
||||
* Job Manager manages long term jobs. These jobs are run in their
|
||||
* own threads.
|
||||
*/
|
||||
|
||||
#ifndef SEAF_JOB_MGR_H
|
||||
#define SEAF_JOB_MGR_H
|
||||
|
||||
struct _SeafJobManager;
|
||||
typedef struct _SeafJobManager SeafJobManager;
|
||||
|
||||
struct _SeafileSession;
|
||||
|
||||
/*
|
||||
The thread func should return the result back by
|
||||
return (void *)result;
|
||||
The result will be passed to JobDoneCallback.
|
||||
*/
|
||||
typedef void* (*JobThreadFunc)(void *data);
|
||||
typedef void (*JobDoneCallback)(void *result);
|
||||
|
||||
SeafJobManager *
|
||||
seaf_job_manager_new (struct _SeafileSession *session, int max_threads);
|
||||
|
||||
void
|
||||
seaf_job_manager_free (struct _SeafJobManager *mgr);
|
||||
|
||||
int
|
||||
seaf_job_manager_schedule_job (struct _SeafJobManager *mgr,
|
||||
JobThreadFunc func,
|
||||
JobDoneCallback done_func,
|
||||
void *data);
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
@ -1,48 +0,0 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#ifndef MERGE_RECURSIVE_H
|
||||
#define MERGE_RECURSIVE_H
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#include "commit-mgr.h"
|
||||
#include "fs-mgr.h"
|
||||
#include "seafile-crypt.h"
|
||||
|
||||
struct merge_options {
|
||||
char repo_id[37];
|
||||
int version;
|
||||
const char *ancestor;
|
||||
const char *branch1;
|
||||
const char *branch2;
|
||||
const char *remote_head;
|
||||
int call_depth;
|
||||
char *worktree;
|
||||
struct index_state *index;
|
||||
GString *obuf;
|
||||
GHashTable *current_file_set;
|
||||
GHashTable *current_directory_set;
|
||||
gboolean recover_merge;
|
||||
gboolean force_merge;
|
||||
SeafileCrypt *crypt;
|
||||
|
||||
/* True if we only want to know the files that would be
|
||||
* updated in this merge, but don't want to update them in the
|
||||
* worktree.
|
||||
*/
|
||||
gboolean collect_blocks_only;
|
||||
BlockList *bl;
|
||||
};
|
||||
|
||||
int merge_recursive(struct merge_options *o,
|
||||
const char *h1_root,
|
||||
const char *h2_root,
|
||||
const char *ca_root,
|
||||
int *clean,
|
||||
char **root_id);
|
||||
|
||||
void init_merge_options(struct merge_options *o);
|
||||
void clear_merge_options(struct merge_options *o);
|
||||
char *write_tree_from_memory(struct merge_options *o);
|
||||
|
||||
#endif
|
432
daemon/merge.c
432
daemon/merge.c
@ -1,432 +0,0 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include <ccnet.h>
|
||||
|
||||
#include "index/index.h"
|
||||
#include "unpack-trees.h"
|
||||
#include "merge-recursive.h"
|
||||
#include "merge.h"
|
||||
|
||||
#include "seafile-session.h"
|
||||
#include "vc-utils.h"
|
||||
#include "vc-common.h"
|
||||
|
||||
#if 0
|
||||
static int
|
||||
print_index (struct index_state *istate)
|
||||
{
|
||||
int i;
|
||||
struct cache_entry *ce;
|
||||
char id[41];
|
||||
g_message ("Totally %u entries in index, version %u.\n",
|
||||
istate->cache_nr, istate->version);
|
||||
for (i = 0; i < istate->cache_nr; ++i) {
|
||||
ce = istate->cache[i];
|
||||
rawdata_to_hex (ce->sha1, id, 20);
|
||||
g_message ("%s, %s, %o, %"G_GUINT64_FORMAT", %s, %d\n",
|
||||
ce->name, id, ce->ce_mode,
|
||||
ce->ce_mtime.sec, ce->modifier, ce_stage(ce));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int
|
||||
do_real_merge (SeafRepo *repo,
|
||||
SeafBranch *head_branch,
|
||||
SeafCommit *head,
|
||||
SeafBranch *remote_branch,
|
||||
SeafCommit *remote,
|
||||
SeafCommit *common,
|
||||
gboolean recover_merge,
|
||||
char **error)
|
||||
{
|
||||
struct merge_options opts;
|
||||
char index_path[SEAF_PATH_MAX];
|
||||
struct index_state istate;
|
||||
char *root_id = NULL;
|
||||
SeafCommit *merged;
|
||||
int ret = 0, clean;
|
||||
|
||||
memset (&istate, 0, sizeof(istate));
|
||||
snprintf (index_path, SEAF_PATH_MAX, "%s/%s", repo->manager->index_dir, repo->id);
|
||||
if (read_index_from (&istate, index_path, repo->version) < 0) {
|
||||
g_warning ("Failed to load index.\n");
|
||||
*error = g_strdup ("Internal error.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
init_merge_options (&opts);
|
||||
memcpy (opts.repo_id, repo->id, 36);
|
||||
opts.version = repo->version;
|
||||
opts.index = &istate;
|
||||
opts.worktree = repo->worktree;
|
||||
opts.ancestor = "common ancestor";
|
||||
opts.branch1 = seaf->session->base.user_name;
|
||||
opts.branch2 = remote->creator_name;
|
||||
opts.remote_head = remote->commit_id;
|
||||
opts.recover_merge = recover_merge;
|
||||
if (repo->encrypted) {
|
||||
opts.crypt = seafile_crypt_new (repo->enc_version,
|
||||
repo->enc_key,
|
||||
repo->enc_iv);
|
||||
}
|
||||
|
||||
ret = merge_recursive (&opts,
|
||||
head->root_id, remote->root_id, common->root_id,
|
||||
&clean, &root_id);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
if (update_index (&istate, index_path) < 0) {
|
||||
*error = g_strdup ("Internal error.\n");
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (clean) {
|
||||
merged = seaf_commit_new (NULL,
|
||||
repo->id,
|
||||
root_id,
|
||||
repo->email ? repo->email
|
||||
: seaf->session->base.user_name,
|
||||
seaf->session->base.id,
|
||||
"Auto merge by system",
|
||||
0);
|
||||
|
||||
merged->parent_id = g_strdup(head->commit_id);
|
||||
merged->second_parent_id = g_strdup(remote->commit_id);
|
||||
merged->new_merge = TRUE;
|
||||
|
||||
seaf_repo_to_commit (repo, merged);
|
||||
|
||||
if (seaf_commit_manager_add_commit (seaf->commit_mgr, merged) < 0) {
|
||||
seaf_commit_unref (merged);
|
||||
*error = g_strdup ("Internal error.\n");
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
seaf_branch_set_commit (head_branch, merged->commit_id);
|
||||
seaf_branch_manager_update_branch (seaf->branch_mgr, head_branch);
|
||||
g_debug ("Auto merged.\n");
|
||||
|
||||
seaf_commit_unref (merged);
|
||||
} else {
|
||||
ret = -1;
|
||||
g_debug ("Auto merge failed.\n");
|
||||
}
|
||||
|
||||
out:
|
||||
if (root_id)
|
||||
g_free (root_id);
|
||||
g_free (opts.crypt);
|
||||
clear_merge_options (&opts);
|
||||
discard_index (&istate);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static SeafCommit *
|
||||
get_common_ancestor_commit (const char *repo_id, int version)
|
||||
{
|
||||
char ca_id[41], head_id[41];
|
||||
SeafCommit *commit;
|
||||
|
||||
if (seaf_repo_manager_get_common_ancestor (seaf->repo_mgr,
|
||||
repo_id, ca_id, head_id) < 0) {
|
||||
g_warning ("Common ancestor commit id is not found in db.\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
commit = seaf_commit_manager_get_commit (seaf->commit_mgr,
|
||||
repo_id, version,
|
||||
ca_id);
|
||||
|
||||
return commit;
|
||||
}
|
||||
|
||||
int
|
||||
merge_branches (SeafRepo *repo, SeafBranch *remote_branch, char **error,
|
||||
int *merge_status)
|
||||
{
|
||||
SeafCommit *common = NULL;
|
||||
SeafCommit *head = NULL, *remote = NULL;
|
||||
int ret = 0;
|
||||
#if 0
|
||||
SeafRepoMergeInfo minfo;
|
||||
#endif
|
||||
|
||||
g_return_val_if_fail (repo && remote_branch && error, -1);
|
||||
|
||||
*merge_status = MERGE_STATUS_UNKNOWN;
|
||||
|
||||
#if 0
|
||||
memset (&minfo, 0, sizeof(minfo));
|
||||
if (seaf_repo_manager_get_merge_info (repo->manager, repo->id, &minfo) < 0) {
|
||||
g_warning ("Failed to get merge status of repo %s.\n", repo->id);
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
head = seaf_commit_manager_get_commit (seaf->commit_mgr,
|
||||
repo->id, repo->version,
|
||||
repo->head->commit_id);
|
||||
if (!head) {
|
||||
*error = g_strdup("Internal error: current branch corrupted.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
remote = seaf_commit_manager_get_commit (seaf->commit_mgr,
|
||||
repo->id, repo->version,
|
||||
remote_branch->commit_id);
|
||||
if (!remote) {
|
||||
*error = g_strdup("Invalid remote branch.\n");
|
||||
ret = -1;
|
||||
goto free_commits;
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* Are we going to recover from the last interrupted merge? */
|
||||
if (minfo.in_merge) {
|
||||
/* We don't need to recover 2 cases, since the last merge was actually finished.
|
||||
* - "master" and "local" are the same;
|
||||
* - index is unmerged.
|
||||
*
|
||||
* The first case is a clean merge; the second case is unclean merge.
|
||||
*/
|
||||
if (strcmp (head->commit_id, remote->commit_id) == 0 ||
|
||||
seaf_repo_is_index_unmerged (repo)) {
|
||||
seaf_repo_manager_clear_merge (repo->manager, repo->id);
|
||||
goto free_commits;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* If not all commits are downloaded, find common ancestor from db;
|
||||
* otherwise we'll use the old method to calculate
|
||||
* common ancestor from local history.
|
||||
*/
|
||||
if (repo->version > 0)
|
||||
common = get_common_ancestor_commit (repo->id, repo->version);
|
||||
else
|
||||
common = get_merge_base (head, remote);
|
||||
|
||||
if (!common) {
|
||||
g_warning ("Cannot find common ancestor\n");
|
||||
*error = g_strdup ("Cannot find common ancestor\n");
|
||||
ret = -1;
|
||||
goto free_commits;
|
||||
}
|
||||
|
||||
/* We use the same logic for normal merge and recover. */
|
||||
|
||||
/* Set in_merge state. */
|
||||
seaf_repo_manager_set_merge (repo->manager, repo->id, remote_branch->commit_id);
|
||||
|
||||
/* printf ("common commit id is %s.\n", common->commit_id); */
|
||||
|
||||
if (strcmp(common->commit_id, remote->commit_id) == 0) {
|
||||
/* We are already up to date. */
|
||||
g_debug ("Already up to date.\n");
|
||||
*merge_status = MERGE_STATUS_UPTODATE;
|
||||
} else if (strcmp(common->commit_id, head->commit_id) == 0) {
|
||||
*merge_status = MERGE_STATUS_FAST_FORWARD;
|
||||
|
||||
/* Fast forward. */
|
||||
if (seaf_repo_checkout_commit (repo, remote, FALSE, error) < 0) {
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
seaf_branch_set_commit (repo->head, remote->commit_id);
|
||||
seaf_branch_manager_update_branch (seaf->branch_mgr, repo->head);
|
||||
|
||||
/* Repo info on the client is in memory. */
|
||||
g_free (repo->name);
|
||||
repo->name = g_strdup(remote->repo_name);
|
||||
g_free (repo->desc);
|
||||
repo->desc = g_strdup(remote->repo_desc);
|
||||
|
||||
g_debug ("Fast forward.\n");
|
||||
} else {
|
||||
/* Not up-to-date and ff, we need a real merge. */
|
||||
*merge_status = MERGE_STATUS_REAL_MERGE;
|
||||
ret = do_real_merge (repo,
|
||||
repo->head, head,
|
||||
remote_branch, remote, common,
|
||||
FALSE,
|
||||
error);
|
||||
}
|
||||
|
||||
out:
|
||||
/* Clear in_merge state, no matter clean or not. */
|
||||
seaf_repo_manager_clear_merge (repo->manager, repo->id);
|
||||
|
||||
free_commits:
|
||||
seaf_commit_unref (common);
|
||||
seaf_commit_unref (remote);
|
||||
seaf_commit_unref (head);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the new blocks that need to be checked out if we ff to @remote.
|
||||
*/
|
||||
static int
|
||||
get_new_blocks_ff (SeafRepo *repo,
|
||||
SeafCommit *head,
|
||||
SeafCommit *remote,
|
||||
BlockList **bl)
|
||||
{
|
||||
SeafRepoManager *mgr = repo->manager;
|
||||
char index_path[SEAF_PATH_MAX];
|
||||
struct tree_desc trees[2];
|
||||
struct unpack_trees_options topts;
|
||||
struct index_state istate;
|
||||
int ret = 0;
|
||||
|
||||
memset (&istate, 0, sizeof(istate));
|
||||
snprintf (index_path, SEAF_PATH_MAX, "%s/%s", mgr->index_dir, repo->id);
|
||||
if (read_index_from (&istate, index_path, repo->version) < 0) {
|
||||
g_warning ("Failed to load index.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
fill_tree_descriptor (repo->id, repo->version, &trees[0], head->root_id);
|
||||
fill_tree_descriptor (repo->id, repo->version, &trees[1], remote->root_id);
|
||||
|
||||
memset(&topts, 0, sizeof(topts));
|
||||
memcpy (topts.repo_id, repo->id, 36);
|
||||
topts.version = repo->version;
|
||||
topts.base = repo->worktree;
|
||||
topts.head_idx = -1;
|
||||
topts.src_index = &istate;
|
||||
topts.update = 1;
|
||||
topts.merge = 1;
|
||||
topts.fn = twoway_merge;
|
||||
|
||||
/* unpack_trees() doesn't update index or worktree. */
|
||||
if (unpack_trees (2, trees, &topts) < 0) {
|
||||
g_warning ("Failed to ff to commit %s.\n", remote->commit_id);
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
*bl = block_list_new ();
|
||||
collect_new_blocks_from_index (repo->id, repo->version, &topts.result, *bl);
|
||||
|
||||
out:
|
||||
tree_desc_free (&trees[0]);
|
||||
tree_desc_free (&trees[1]);
|
||||
discard_index (&istate);
|
||||
discard_index (&topts.result);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the new blocks that need to be checked out if we do a real merge.
|
||||
*/
|
||||
static int
|
||||
get_new_blocks_merge (SeafRepo *repo,
|
||||
SeafCommit *head,
|
||||
SeafCommit *remote,
|
||||
SeafCommit *common,
|
||||
BlockList **bl)
|
||||
{
|
||||
struct merge_options opts;
|
||||
char index_path[SEAF_PATH_MAX];
|
||||
struct index_state istate;
|
||||
int ret, clean;
|
||||
|
||||
memset (&istate, 0, sizeof(istate));
|
||||
snprintf (index_path, SEAF_PATH_MAX, "%s/%s", repo->manager->index_dir, repo->id);
|
||||
if (read_index_from (&istate, index_path, repo->version) < 0) {
|
||||
g_warning ("Failed to load index.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
init_merge_options (&opts);
|
||||
memcpy (opts.repo_id, repo->id, 36);
|
||||
opts.version = repo->version;
|
||||
opts.index = &istate;
|
||||
opts.worktree = repo->worktree;
|
||||
opts.ancestor = "common ancestor";
|
||||
opts.branch1 = seaf->session->base.user_name;
|
||||
opts.branch2 = remote->creator_name;
|
||||
opts.collect_blocks_only = TRUE;
|
||||
|
||||
*bl = block_list_new();
|
||||
opts.bl = *bl;
|
||||
|
||||
ret = merge_recursive (&opts,
|
||||
head->root_id, remote->root_id, common->root_id,
|
||||
&clean, NULL);
|
||||
|
||||
clear_merge_options (&opts);
|
||||
discard_index (&istate);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the list of new blocks that would be checked out after
|
||||
* we merge with a branch headed by @remote.
|
||||
*
|
||||
* This function should be called before downloading any block
|
||||
* if the repo is set to not preserving history. In this case,
|
||||
* we don't want to download any block that will not be checked
|
||||
* out to the worktree (i.e. data from any historical commits).
|
||||
*
|
||||
* Return 0 if successfully calculate the block list, -1 otherwise.
|
||||
* If there is no new block to download, *@bl will be set to NULL;
|
||||
* otherwise it's set to the block list.
|
||||
*/
|
||||
int
|
||||
merge_get_new_block_list (SeafRepo *repo, SeafCommit *remote, BlockList **bl)
|
||||
{
|
||||
SeafCommit *common = NULL;
|
||||
SeafCommit *head = NULL;
|
||||
int ret = 0;
|
||||
|
||||
head = seaf_commit_manager_get_commit (seaf->commit_mgr,
|
||||
repo->id, repo->version,
|
||||
repo->head->commit_id);
|
||||
if (!head) {
|
||||
g_warning ("current branch corrupted.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* If not all commits are downloaded, get common ancestor from db;
|
||||
* otherwise we'll use the old method to calculate
|
||||
* common ancestor from local history.
|
||||
*/
|
||||
if (repo->version > 0)
|
||||
common = get_common_ancestor_commit (repo->id, repo->version);
|
||||
else
|
||||
common = get_merge_base (head, remote);
|
||||
|
||||
if (!common) {
|
||||
g_warning ("Cannot find common ancestor\n");
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (strcmp(common->commit_id, remote->commit_id) == 0) {
|
||||
/* We are already up to date. No new block. */
|
||||
*bl = NULL;
|
||||
} else if (strcmp(common->commit_id, head->commit_id) == 0) {
|
||||
/* Fast forward. */
|
||||
ret = get_new_blocks_ff (repo, head, remote, bl);
|
||||
} else {
|
||||
/* Not up-to-date and ff, we need a real merge. */
|
||||
ret = get_new_blocks_merge (repo, head, remote, common, bl);
|
||||
}
|
||||
|
||||
out:
|
||||
seaf_commit_unref (common);
|
||||
seaf_commit_unref (head);
|
||||
|
||||
return ret;
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#ifndef MERGE_H
|
||||
#define MERGE_H
|
||||
|
||||
#include "repo-mgr.h"
|
||||
#include "commit-mgr.h"
|
||||
#include "branch-mgr.h"
|
||||
#include "fs-mgr.h"
|
||||
|
||||
int
|
||||
merge_branches (SeafRepo *repo, SeafBranch *remote_branch, char **error,
|
||||
gboolean *real_merge);
|
||||
|
||||
int
|
||||
merge_get_new_block_list (SeafRepo *repo, SeafCommit *remote, BlockList **bl);
|
||||
|
||||
#endif
|
@ -1,75 +0,0 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include "check-protocol-proc.h"
|
||||
|
||||
#define DEBUG_FLAG SEAFILE_DEBUG_SYNC
|
||||
#include "log.h"
|
||||
|
||||
G_DEFINE_TYPE (SeafileCheckProtocolProc, seafile_check_protocol_proc, CCNET_TYPE_PROCESSOR)
|
||||
|
||||
|
||||
static int
|
||||
check_protocol_start (CcnetProcessor *processor, int argc, char **argv);
|
||||
|
||||
static void
|
||||
handle_response (CcnetProcessor *processor,
|
||||
char *code, char *code_msg,
|
||||
char *content, int clen);
|
||||
|
||||
static void
|
||||
seafile_check_protocol_proc_class_init (SeafileCheckProtocolProcClass *klass)
|
||||
{
|
||||
CcnetProcessorClass *proc_class = CCNET_PROCESSOR_CLASS (klass);
|
||||
|
||||
proc_class->name = "seafile-check-protocol";
|
||||
proc_class->start = check_protocol_start;
|
||||
proc_class->handle_response = handle_response;
|
||||
}
|
||||
|
||||
static void
|
||||
seafile_check_protocol_proc_init (SeafileCheckProtocolProc *processor)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
check_protocol_start (CcnetProcessor *processor, int argc, char **argv)
|
||||
{
|
||||
if (argc != 0) {
|
||||
seaf_warning ("[sync-repo] argc should be 0.\n");
|
||||
ccnet_processor_done (processor, FALSE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
char buf[256];
|
||||
|
||||
snprintf (buf, 256, "remote %s seafile-check-protocol-slave", processor->peer_id);
|
||||
|
||||
ccnet_processor_send_request (processor, buf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void handle_response (CcnetProcessor *processor,
|
||||
char *code, char *code_msg,
|
||||
char *content, int clen)
|
||||
{
|
||||
SeafileCheckProtocolProc *proc = (SeafileCheckProtocolProc *)processor;
|
||||
|
||||
if (memcmp (code, SC_OK, 3) == 0) {
|
||||
|
||||
if (content[clen-1] != '\0') {
|
||||
seaf_warning ("[check-protocol] Response not end with NULL\n");
|
||||
ccnet_processor_done (processor, FALSE);
|
||||
return;
|
||||
}
|
||||
|
||||
proc->protocol_version = atoi(content);
|
||||
|
||||
ccnet_processor_done (processor, TRUE);
|
||||
} else
|
||||
ccnet_processor_done (processor, FALSE);
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#ifndef SEAFILE_CHECK_PROTOCOL_PROC_H
|
||||
#define SEAFILE_CHECK_PROTOCOL_PROC_H
|
||||
|
||||
#include <glib-object.h>
|
||||
#include <ccnet.h>
|
||||
|
||||
#define SEAFILE_TYPE_CHECK_PROTOCOL_PROC (seafile_check_protocol_proc_get_type ())
|
||||
#define SEAFILE_CHECK_PROTOCOL_PROC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SEAFILE_TYPE_CHECK_PROTOCOL_PROC, SeafileCheckProtocolProc))
|
||||
#define SEAFILE_IS_CHECK_PROTOCOL_PROC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SEAFILE_TYPE_CHECK_PROTOCOL_PROC))
|
||||
#define SEAFILE_CHECK_PROTOCOL_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SEAFILE_TYPE_CHECK_PROTOCOL_PROC, SeafileCheckProtocolProcClass))
|
||||
#define IS_SEAFILE_CHECK_PROTOCOL_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SEAFILE_TYPE_CHECK_PROTOCOL_PROC))
|
||||
#define SEAFILE_CHECK_PROTOCOL_PROC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SEAFILE_TYPE_CHECK_PROTOCOL_PROC, SeafileCheckProtocolProcClass))
|
||||
|
||||
typedef struct _SeafileCheckProtocolProc SeafileCheckProtocolProc;
|
||||
typedef struct _SeafileCheckProtocolProcClass SeafileCheckProtocolProcClass;
|
||||
|
||||
struct _SeafileCheckProtocolProc {
|
||||
CcnetProcessor parent_instance;
|
||||
|
||||
int protocol_version;
|
||||
};
|
||||
|
||||
struct _SeafileCheckProtocolProcClass {
|
||||
CcnetProcessorClass parent_class;
|
||||
};
|
||||
|
||||
GType seafile_check_protocol_proc_get_type ();
|
||||
|
||||
#endif
|
@ -1,335 +0,0 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#ifndef USE_GPL_CRYPTO
|
||||
|
||||
#include <string.h>
|
||||
#include <ccnet.h>
|
||||
#include <openssl/aes.h>
|
||||
#include <openssl/evp.h>
|
||||
|
||||
#include "seafile-session.h"
|
||||
#include "vc-common.h"
|
||||
#include "seafile-crypt.h"
|
||||
#include "log.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include "check-tx-v3-proc.h"
|
||||
|
||||
#define SC_GET_TOKEN "301"
|
||||
#define SS_GET_TOKEN "Get token"
|
||||
#define SC_PUT_TOKEN "302"
|
||||
#define SS_PUT_TOKEN "Put token"
|
||||
#define SC_GET_VERSION "303"
|
||||
#define SS_GET_VERSION "Get version"
|
||||
#define SC_VERSION "304"
|
||||
#define SS_VERSION "Version"
|
||||
|
||||
#define SC_ACCESS_DENIED "401"
|
||||
#define SS_ACCESS_DENIED "Access denied"
|
||||
#define SC_PROTOCOL_MISMATCH "405"
|
||||
#define SS_PROTOCOL_MISMATCH "Protocol version mismatch"
|
||||
|
||||
/* Only for upload */
|
||||
#define SC_QUOTA_ERROR "402"
|
||||
#define SS_QUOTA_ERROR "Failed to get quota"
|
||||
#define SC_QUOTA_FULL "403"
|
||||
#define SS_QUOTA_FULL "storage for the repo's owner is full"
|
||||
|
||||
/* Only for download */
|
||||
#define SC_BAD_REPO "406"
|
||||
#define SS_BAD_REPO "Repo doesn't exist"
|
||||
|
||||
enum {
|
||||
CHECK_TX_TYPE_UPLOAD,
|
||||
CHECK_TX_TYPE_DOWNLOAD,
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (SeafileCheckTxV3Proc, seafile_check_tx_v3_proc, CCNET_TYPE_PROCESSOR)
|
||||
|
||||
static int start (CcnetProcessor *processor, int argc, char **argv);
|
||||
static void handle_response (CcnetProcessor *processor,
|
||||
char *code, char *code_msg,
|
||||
char *content, int clen);
|
||||
|
||||
static void
|
||||
release_resource(CcnetProcessor *processor)
|
||||
{
|
||||
CCNET_PROCESSOR_CLASS (seafile_check_tx_v3_proc_parent_class)->release_resource (processor);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
seafile_check_tx_v3_proc_class_init (SeafileCheckTxV3ProcClass *klass)
|
||||
{
|
||||
CcnetProcessorClass *proc_class = CCNET_PROCESSOR_CLASS (klass);
|
||||
|
||||
proc_class->name = "check-tx-proc-v3";
|
||||
proc_class->start = start;
|
||||
proc_class->handle_response = handle_response;
|
||||
proc_class->release_resource = release_resource;
|
||||
}
|
||||
|
||||
static void
|
||||
seafile_check_tx_v3_proc_init (SeafileCheckTxV3Proc *processor)
|
||||
{
|
||||
}
|
||||
|
||||
/* token -> AES encrypt with session key -> rawdata_to_hex -> output */
|
||||
static char *
|
||||
encrypt_token (CcnetProcessor *processor, const char *token)
|
||||
{
|
||||
CcnetPeer *peer = NULL;
|
||||
char *enc_out = NULL;
|
||||
SeafileCrypt *crypt = NULL;
|
||||
unsigned char key[16], iv[16];
|
||||
int len;
|
||||
char *output = NULL;
|
||||
|
||||
if (!token)
|
||||
goto out;
|
||||
|
||||
peer = ccnet_get_peer(seaf->ccnetrpc_client, processor->peer_id);
|
||||
if (!peer || !peer->session_key) {
|
||||
seaf_warning ("[check tx v3] peer or peer session key not exist\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
EVP_BytesToKey (EVP_aes_128_cbc(), /* cipher mode */
|
||||
EVP_sha1(), /* message digest */
|
||||
NULL, /* slat */
|
||||
(unsigned char*)peer->session_key,
|
||||
strlen(peer->session_key),
|
||||
1, /* iteration times */
|
||||
key, /* the derived key */
|
||||
iv); /* IV, initial vector */
|
||||
|
||||
crypt = seafile_crypt_new (1, key, iv);
|
||||
|
||||
/* encrypt the token with session key, including the trailing null byte */
|
||||
if (seafile_encrypt (&enc_out, &len, token, strlen(token) + 1, crypt) < 0) {
|
||||
seaf_warning ("[check tx v3] failed to encrypt token\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
output = g_malloc (len * 2 + 1);
|
||||
rawdata_to_hex ((unsigned char *)enc_out, output, len);
|
||||
output[len * 2] = '\0';
|
||||
|
||||
|
||||
out:
|
||||
g_free (crypt);
|
||||
g_free (enc_out);
|
||||
if (peer)
|
||||
g_object_unref(peer);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
start (CcnetProcessor *processor, int argc, char **argv)
|
||||
{
|
||||
SeafileCheckTxV3Proc *proc = (SeafileCheckTxV3Proc *)processor;
|
||||
TransferTask *task = proc->task;
|
||||
char *type, *enc_token;
|
||||
GString *buf;
|
||||
|
||||
if (argc != 1) {
|
||||
transition_state_to_error (task, TASK_ERR_CHECK_UPLOAD_START);
|
||||
ccnet_processor_done (processor, FALSE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
type = argv[0];
|
||||
if (strcmp (type, "upload") == 0)
|
||||
proc->type = CHECK_TX_TYPE_UPLOAD;
|
||||
else
|
||||
proc->type = CHECK_TX_TYPE_DOWNLOAD;
|
||||
|
||||
enc_token = encrypt_token (processor, task->token);
|
||||
if (!enc_token) {
|
||||
transition_state_to_error (task, TASK_ERR_CHECK_UPLOAD_START);
|
||||
ccnet_processor_done (processor, FALSE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
buf = g_string_new(NULL);
|
||||
g_string_append_printf (buf,
|
||||
"remote %s seafile-check-tx-slave-v3 %s %d %s %s %s",
|
||||
processor->peer_id, type, CURRENT_PROTO_VERSION,
|
||||
task->repo_id, task->to_branch, enc_token);
|
||||
|
||||
ccnet_processor_send_request (processor, buf->str);
|
||||
|
||||
g_free (enc_token);
|
||||
g_string_free (buf, TRUE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
handle_upload_ok (CcnetProcessor *processor, TransferTask *task,
|
||||
char *content, int clen)
|
||||
{
|
||||
if (clen == 0) {
|
||||
ccnet_processor_send_update (processor,
|
||||
SC_GET_TOKEN, SS_GET_TOKEN,
|
||||
NULL, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (clen != 41 || content[clen-1] != '\0') {
|
||||
seaf_warning ("Bad response content.\n");
|
||||
transfer_task_set_error (task, TASK_ERR_UNKNOWN);
|
||||
ccnet_processor_send_update (processor, SC_BAD_ARGS, SS_BAD_ARGS, NULL, 0);
|
||||
ccnet_processor_done (processor, FALSE);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Ignore the returned remote head id, just use the head of master branch.
|
||||
* For protocol version >= 6, the complete hitstory is not downloaded, so
|
||||
* there is no way to check fast forward on the client. For protocol version
|
||||
* < 6, the server will check fast forward anyway.
|
||||
*/
|
||||
SeafBranch *master = seaf_branch_manager_get_branch (seaf->branch_mgr,
|
||||
task->repo_id, "master");
|
||||
if (!master) {
|
||||
seaf_warning ("Cannot find branch master for repo %s.\n", task->repo_id);
|
||||
ccnet_processor_send_update (processor, SC_SHUTDOWN, SS_SHUTDOWN, NULL, 0);
|
||||
ccnet_processor_done (processor, FALSE);
|
||||
return;
|
||||
}
|
||||
memcpy (task->remote_head, master->commit_id, 40);
|
||||
seaf_branch_unref (master);
|
||||
|
||||
ccnet_processor_send_update (processor,
|
||||
SC_GET_TOKEN, SS_GET_TOKEN,
|
||||
NULL, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
handle_download_ok (CcnetProcessor *processor, TransferTask *task,
|
||||
char *content, int clen)
|
||||
{
|
||||
if (clen != 41 || content[clen-1] != '\0') {
|
||||
seaf_warning ("Bad response content.\n");
|
||||
transfer_task_set_error (task, TASK_ERR_UNKNOWN);
|
||||
ccnet_processor_send_update (processor, SC_BAD_ARGS, SS_BAD_ARGS, NULL, 0);
|
||||
ccnet_processor_done (processor, FALSE);
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy (task->head, content, 41);
|
||||
|
||||
ccnet_processor_send_update (processor,
|
||||
SC_GET_TOKEN, SS_GET_TOKEN,
|
||||
NULL, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
set_download_head_info (TransferTask *task)
|
||||
{
|
||||
/* If the last download was interrupted in the fetch and checkout stage,
|
||||
* resume last download.
|
||||
*/
|
||||
char *last_head = seaf_repo_manager_get_repo_property (seaf->repo_mgr,
|
||||
task->repo_id,
|
||||
REPO_PROP_DOWNLOAD_HEAD);
|
||||
if (last_head && strcmp (last_head, EMPTY_SHA1) != 0)
|
||||
memcpy (task->head, last_head, 41);
|
||||
g_free (last_head);
|
||||
}
|
||||
|
||||
static void
|
||||
handle_response (CcnetProcessor *processor,
|
||||
char *code, char *code_msg,
|
||||
char *content, int clen)
|
||||
{
|
||||
SeafileCheckTxV3Proc *proc = (SeafileCheckTxV3Proc *)processor;
|
||||
TransferTask *task = proc->task;
|
||||
|
||||
if (strncmp(code, SC_OK, 3) == 0) {
|
||||
if (proc->type == CHECK_TX_TYPE_UPLOAD)
|
||||
handle_upload_ok (processor, task, content, clen);
|
||||
else
|
||||
handle_download_ok (processor, task, content, clen);
|
||||
} else if (strncmp (code, SC_PUT_TOKEN, 3) == 0) {
|
||||
/* In LAN sync, we don't use session token. */
|
||||
if (clen == 0) {
|
||||
ccnet_processor_done (processor, TRUE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (content[clen-1] != '\0') {
|
||||
seaf_warning ("Bad response content.\n");
|
||||
transfer_task_set_error (task, TASK_ERR_UNKNOWN);
|
||||
ccnet_processor_send_update (processor, SC_BAD_ARGS, SS_BAD_ARGS,
|
||||
NULL, 0);
|
||||
ccnet_processor_done (processor, FALSE);
|
||||
return;
|
||||
}
|
||||
task->session_token = g_strdup (content);
|
||||
|
||||
ccnet_processor_send_update (processor, SC_GET_VERSION, SS_GET_VERSION,
|
||||
NULL, 0);
|
||||
} else if (strncmp (code, SC_VERSION, 3) == 0) {
|
||||
int server_version = atoi(content);
|
||||
/* There is a bug in block transfer in version 4, so it's not supported. */
|
||||
if (server_version == 4)
|
||||
server_version = 3;
|
||||
task->protocol_version = MIN (server_version, CURRENT_PROTO_VERSION);
|
||||
|
||||
if (task->protocol_version < 5) {
|
||||
seaf_warning ("Deprecated server protocol version %d.\n",
|
||||
task->protocol_version);
|
||||
transfer_task_set_error (task, TASK_ERR_DEPRECATED_SERVER);
|
||||
ccnet_processor_done (processor, FALSE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (task->repo_version == 0)
|
||||
task->protocol_version = 5;
|
||||
else if (task->protocol_version == 5) {
|
||||
/* Syncing version 1 reop with 2.x server is not supported.
|
||||
* Actually version 1 repo can only be created by 3.x servers.
|
||||
* If version 1 repos exist on 2.x server, it means a down-grade
|
||||
* operation has been performed, which is not supported.
|
||||
*/
|
||||
seaf_warning ("Syncing version %d repo with protocol version %d "
|
||||
"is not supported.\n",
|
||||
task->repo_version, task->protocol_version);
|
||||
transfer_task_set_error (task, TASK_ERR_DEPRECATED_SERVER);
|
||||
ccnet_processor_done (processor, FALSE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (task->protocol_version >= 7 && !task->server_side_merge)
|
||||
task->protocol_version = 6;
|
||||
|
||||
if (task->protocol_version >= 7 && task->type == TASK_TYPE_DOWNLOAD)
|
||||
set_download_head_info (task);
|
||||
|
||||
seaf_message ("repo version is %d, protocol version is %d.\n",
|
||||
task->repo_version, task->protocol_version);
|
||||
ccnet_processor_done (processor, TRUE);
|
||||
} else {
|
||||
seaf_warning ("[check tx v3] Bad response: %s %s", code, code_msg);
|
||||
if (strncmp(code, SC_ACCESS_DENIED, 3) == 0)
|
||||
transfer_task_set_error (task, TASK_ERR_ACCESS_DENIED);
|
||||
else if (strncmp(code, SC_QUOTA_ERROR, 3) == 0)
|
||||
transfer_task_set_error (task, TASK_ERR_CHECK_QUOTA);
|
||||
else if (strncmp(code, SC_QUOTA_FULL, 3) == 0)
|
||||
transfer_task_set_error (task, TASK_ERR_QUOTA_FULL);
|
||||
else if (strncmp(code, SC_PROTOCOL_MISMATCH, 3) == 0)
|
||||
transfer_task_set_error (task, TASK_ERR_PROTOCOL_VERSION);
|
||||
else if (strncmp(code, SC_BAD_REPO, 3) == 0)
|
||||
transfer_task_set_error (task, TASK_ERR_BAD_REPO_ID);
|
||||
else
|
||||
transfer_task_set_error (task, TASK_ERR_UNKNOWN);
|
||||
ccnet_processor_done (processor, FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@ -1,34 +0,0 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#ifndef SEAFILE_CHECK_TX_V3_PROC_H
|
||||
#define SEAFILE_CHECK_TX_V3_PROC_H
|
||||
|
||||
#include <glib-object.h>
|
||||
#include <ccnet/processor.h>
|
||||
|
||||
#include "transfer-mgr.h"
|
||||
|
||||
#define SEAFILE_TYPE_CHECK_TX_V3_PROC (seafile_check_tx_v3_proc_get_type ())
|
||||
#define SEAFILE_CHECK_TX_V3_PROC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SEAFILE_TYPE_CHECK_TX_V3_PROC, SeafileCheckTxV3Proc))
|
||||
#define SEAFILE_IS_CHECK_TX_V3_PROC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SEAFILE_TYPE_CHECK_TX_PROC))
|
||||
#define SEAFILE_CHECK_TX_V3_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SEAFILE_TYPE_CHECK_TX_V3_PROC, SeafileCheckTxV3ProcClass))
|
||||
#define IS_SEAFILE_CHECK_TX_V3_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SEAFILE_TYPE_CHECK_TX_V3_PROC))
|
||||
#define SEAFILE_CHECK_TX_V3_PROC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SEAFILE_TYPE_CHECK_TX_V3_PROC, SeafileCheckTxV3ProcClass))
|
||||
|
||||
typedef struct _SeafileCheckTxV3Proc SeafileCheckTxV3Proc;
|
||||
typedef struct _SeafileCheckTxV3ProcClass SeafileCheckTxV3ProcClass;
|
||||
|
||||
struct _SeafileCheckTxV3Proc {
|
||||
CcnetProcessor parent_instance;
|
||||
|
||||
int type;
|
||||
TransferTask *task;
|
||||
};
|
||||
|
||||
struct _SeafileCheckTxV3ProcClass {
|
||||
CcnetProcessorClass parent_class;
|
||||
};
|
||||
|
||||
GType seafile_check_tx_v3_proc_get_type ();
|
||||
|
||||
#endif
|
@ -1,178 +0,0 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
/*
|
||||
* checkbl-proc start
|
||||
* --------------------------->
|
||||
*
|
||||
* OK
|
||||
* <--------------------------
|
||||
*
|
||||
* Block list segment 1
|
||||
* -------------------------->
|
||||
*
|
||||
* Non-exist block list
|
||||
* <-------------------------
|
||||
*
|
||||
* Block list segment 2
|
||||
* -------------------------->
|
||||
*
|
||||
* Non-exist block list
|
||||
* <-------------------------
|
||||
*
|
||||
* Block list end
|
||||
* ------------------------->
|
||||
*
|
||||
*/
|
||||
|
||||
#define SC_BLOCK_LIST "301"
|
||||
#define SS_BLOCK_LIST "Block list"
|
||||
#define SC_NEED_BLOCKS "302"
|
||||
#define SS_NEED_BLOCKS "Needed blocks"
|
||||
#define SC_BLOCK_LIST_END "303"
|
||||
#define SS_BLOCK_LIST_END "Block list end"
|
||||
|
||||
#include "checkbl-proc.h"
|
||||
#define DEBUG_FLAG SEAFILE_DEBUG_TRANSFER
|
||||
#include "log.h"
|
||||
|
||||
typedef struct {
|
||||
int offset;
|
||||
} SeafileCheckblProcPriv;
|
||||
|
||||
#define GET_PRIV(o) \
|
||||
(G_TYPE_INSTANCE_GET_PRIVATE ((o), SEAFILE_TYPE_CHECKBL_PROC, SeafileCheckblProcPriv))
|
||||
|
||||
#define USE_PRIV \
|
||||
SeafileCheckblProcPriv *priv = GET_PRIV(processor);
|
||||
|
||||
|
||||
G_DEFINE_TYPE (SeafileCheckblProc, seafile_checkbl_proc, CCNET_TYPE_PROCESSOR)
|
||||
|
||||
static int start (CcnetProcessor *processor, int argc, char **argv);
|
||||
static void handle_response (CcnetProcessor *processor,
|
||||
char *code, char *code_msg,
|
||||
char *content, int clen);
|
||||
|
||||
static void
|
||||
release_resource(CcnetProcessor *processor)
|
||||
{
|
||||
/* FILL IT */
|
||||
|
||||
CCNET_PROCESSOR_CLASS (seafile_checkbl_proc_parent_class)->release_resource (processor);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
seafile_checkbl_proc_class_init (SeafileCheckblProcClass *klass)
|
||||
{
|
||||
CcnetProcessorClass *proc_class = CCNET_PROCESSOR_CLASS (klass);
|
||||
|
||||
proc_class->start = start;
|
||||
proc_class->handle_response = handle_response;
|
||||
proc_class->release_resource = release_resource;
|
||||
|
||||
g_type_class_add_private (klass, sizeof (SeafileCheckblProcPriv));
|
||||
}
|
||||
|
||||
static void
|
||||
seafile_checkbl_proc_init (SeafileCheckblProc *processor)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
start (CcnetProcessor *processor, int argc, char **argv)
|
||||
{
|
||||
SeafileCheckblProc *proc = (SeafileCheckblProc *)processor;
|
||||
TransferTask *task = proc->task;
|
||||
GString *buf = g_string_new ("");
|
||||
|
||||
if (!proc->send_session_token)
|
||||
g_string_printf (buf, "remote %s seafile-checkbl", processor->peer_id);
|
||||
else
|
||||
g_string_printf (buf, "remote %s seafile-checkbl %s",
|
||||
processor->peer_id, task->session_token);
|
||||
ccnet_processor_send_request (processor, buf->str);
|
||||
g_string_free (buf, TRUE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define BLOCK_LIST_SEGMENT_N_BLOCKS 120
|
||||
#define BLOCK_LIST_SEGMENT_LEN 40 * 120
|
||||
|
||||
static void
|
||||
send_block_list_segment (CcnetProcessor *processor, BlockList *block_list)
|
||||
{
|
||||
USE_PRIV;
|
||||
int len, limit;
|
||||
char buf[BLOCK_LIST_SEGMENT_LEN];
|
||||
char *ptr;
|
||||
|
||||
if (priv->offset == block_list->n_blocks) {
|
||||
ccnet_processor_send_update (processor, SC_BLOCK_LIST_END, SS_BLOCK_LIST_END,
|
||||
NULL, 0);
|
||||
ccnet_processor_done (processor, TRUE);
|
||||
return;
|
||||
}
|
||||
|
||||
len = MIN (block_list->n_blocks - priv->offset, BLOCK_LIST_SEGMENT_N_BLOCKS);
|
||||
limit = priv->offset + len;
|
||||
|
||||
for (ptr = buf; priv->offset < limit; ++(priv->offset)) {
|
||||
char *block_id = g_ptr_array_index (block_list->block_ids, priv->offset);
|
||||
memcpy (ptr, block_id, 40);
|
||||
ptr += 40;
|
||||
}
|
||||
|
||||
seaf_debug ("Send %d block ids in block list segment.\n", len);
|
||||
ccnet_processor_send_update (processor, SC_BLOCK_LIST, SS_BLOCK_LIST,
|
||||
buf, len * 40);
|
||||
}
|
||||
|
||||
static void
|
||||
process_needed_blocks (CcnetProcessor *processor, TransferTask *task,
|
||||
char *content, int clen)
|
||||
{
|
||||
if (clen == 0) {
|
||||
seaf_debug ("No block is needed on the server.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (clen % 40 != 0) {
|
||||
seaf_warning ("Bad block list length %d.\n", clen);
|
||||
ccnet_processor_send_update (processor, SC_SHUTDOWN, SS_SHUTDOWN, NULL, 0);
|
||||
ccnet_processor_done (processor, FALSE);
|
||||
return;
|
||||
}
|
||||
|
||||
seaf_debug ("%d blocks are needed by the server.\n", clen/40);
|
||||
|
||||
int offset = 0;
|
||||
while (offset < clen) {
|
||||
char *block_id = g_new (char, 41);
|
||||
memcpy (block_id, &content[offset], 40);
|
||||
block_id[40] = 0;
|
||||
offset += 40;
|
||||
g_queue_push_tail (task->block_ids, block_id);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
handle_response (CcnetProcessor *processor,
|
||||
char *code, char *code_msg,
|
||||
char *content, int clen)
|
||||
{
|
||||
SeafileCheckblProc *proc = (SeafileCheckblProc *)processor;
|
||||
TransferTask *task = proc->task;
|
||||
|
||||
if (memcmp (code, SC_OK, 3) == 0) {
|
||||
send_block_list_segment (processor, task->block_list);
|
||||
} else if (memcmp (code, SC_NEED_BLOCKS, 3) == 0) {
|
||||
process_needed_blocks (processor, task, content, clen);
|
||||
send_block_list_segment (processor, task->block_list);
|
||||
} else {
|
||||
seaf_warning ("Bad response: %s %s.\n", code, code_msg);
|
||||
ccnet_processor_done (processor, FALSE);
|
||||
}
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#ifndef SEAFILE_CHECKBL_PROC_H
|
||||
#define SEAFILE_CHECKBL_PROC_H
|
||||
|
||||
#include <glib-object.h>
|
||||
#include <ccnet/processor.h>
|
||||
|
||||
#include "transfer-mgr.h"
|
||||
|
||||
#define SEAFILE_TYPE_CHECKBL_PROC (seafile_checkbl_proc_get_type ())
|
||||
#define SEAFILE_CHECKBL_PROC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SEAFILE_TYPE_CHECKBL_PROC, SeafileCheckblProc))
|
||||
#define SEAFILE_IS_CHECKBL_PROC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SEAFILE_TYPE_CHECKBL_PROC))
|
||||
#define SEAFILE_CHECKBL_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SEAFILE_TYPE_CHECKBL_PROC, SeafileCheckblProcClass))
|
||||
#define IS_SEAFILE_CHECKBL_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SEAFILE_TYPE_CHECKBL_PROC))
|
||||
#define SEAFILE_CHECKBL_PROC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SEAFILE_TYPE_CHECKBL_PROC, SeafileCheckblProcClass))
|
||||
|
||||
typedef struct _SeafileCheckblProc SeafileCheckblProc;
|
||||
typedef struct _SeafileCheckblProcClass SeafileCheckblProcClass;
|
||||
|
||||
struct _SeafileCheckblProc {
|
||||
CcnetProcessor parent_instance;
|
||||
|
||||
TransferTask *task;
|
||||
gboolean send_session_token;
|
||||
};
|
||||
|
||||
struct _SeafileCheckblProcClass {
|
||||
CcnetProcessorClass parent_class;
|
||||
};
|
||||
|
||||
GType seafile_checkbl_proc_get_type ();
|
||||
|
||||
#endif
|
||||
|
@ -1,68 +0,0 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include "checkff-proc.h"
|
||||
#define DEBUG_FLAG SEAFILE_DEBUG_TRANSFER
|
||||
#include "log.h"
|
||||
|
||||
G_DEFINE_TYPE (SeafileCheckffProc, seafile_checkff_proc, CCNET_TYPE_PROCESSOR)
|
||||
|
||||
static int start (CcnetProcessor *processor, int argc, char **argv);
|
||||
static void handle_response (CcnetProcessor *processor,
|
||||
char *code, char *code_msg,
|
||||
char *content, int clen);
|
||||
|
||||
static void
|
||||
release_resource(CcnetProcessor *processor)
|
||||
{
|
||||
/* FILL IT */
|
||||
|
||||
CCNET_PROCESSOR_CLASS (seafile_checkff_proc_parent_class)->release_resource (processor);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
seafile_checkff_proc_class_init (SeafileCheckffProcClass *klass)
|
||||
{
|
||||
CcnetProcessorClass *proc_class = CCNET_PROCESSOR_CLASS (klass);
|
||||
|
||||
proc_class->start = start;
|
||||
proc_class->handle_response = handle_response;
|
||||
proc_class->release_resource = release_resource;
|
||||
}
|
||||
|
||||
static void
|
||||
seafile_checkff_proc_init (SeafileCheckffProc *processor)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
start (CcnetProcessor *processor, int argc, char **argv)
|
||||
{
|
||||
char buf[256];
|
||||
|
||||
snprintf (buf, sizeof(buf),
|
||||
"remote %s seafile-checkff %s %s", processor->peer_id,
|
||||
argv[0], argv[1]);
|
||||
ccnet_processor_send_request (processor, buf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
handle_response (CcnetProcessor *processor,
|
||||
char *code, char *code_msg,
|
||||
char *content, int clen)
|
||||
{
|
||||
SeafileCheckffProc *proc = (SeafileCheckffProc *)processor;
|
||||
|
||||
if (memcmp (code, SC_OK, 3) == 0) {
|
||||
proc->is_fast_forward = (atoi (content) != 0);
|
||||
ccnet_processor_done (processor, TRUE);
|
||||
} else {
|
||||
seaf_warning ("Bad response: %s %s.\n", code, code_msg);
|
||||
ccnet_processor_done (processor, FALSE);
|
||||
}
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#ifndef SEAFILE_CHECKFF_PROC_H
|
||||
#define SEAFILE_CHECKFF_PROC_H
|
||||
|
||||
#include <glib-object.h>
|
||||
#include <ccnet/processor.h>
|
||||
|
||||
#define SEAFILE_TYPE_CHECKFF_PROC (seafile_checkff_proc_get_type ())
|
||||
#define SEAFILE_CHECKFF_PROC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SEAFILE_TYPE_CHECKFF_PROC, SeafileCheckffProc))
|
||||
#define SEAFILE_IS_CHECKFF_PROC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SEAFILE_TYPE_CHECKFF_PROC))
|
||||
#define SEAFILE_CHECKFF_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SEAFILE_TYPE_CHECKFF_PROC, SeafileCheckffProcClass))
|
||||
#define IS_SEAFILE_CHECKFF_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SEAFILE_TYPE_CHECKFF_PROC))
|
||||
#define SEAFILE_CHECKFF_PROC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SEAFILE_TYPE_CHECKFF_PROC, SeafileCheckffProcClass))
|
||||
|
||||
typedef struct _SeafileCheckffProc SeafileCheckffProc;
|
||||
typedef struct _SeafileCheckffProcClass SeafileCheckffProcClass;
|
||||
|
||||
struct _SeafileCheckffProc {
|
||||
CcnetProcessor parent_instance;
|
||||
|
||||
gboolean is_fast_forward;
|
||||
};
|
||||
|
||||
struct _SeafileCheckffProcClass {
|
||||
CcnetProcessorClass parent_class;
|
||||
};
|
||||
|
||||
GType seafile_checkff_proc_get_type ();
|
||||
|
||||
#endif
|
||||
|
@ -1,290 +0,0 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#define DEBUG_FLAG SEAFILE_DEBUG_TRANSFER
|
||||
#include "log.h"
|
||||
|
||||
#include <ccnet.h>
|
||||
#include "utils.h"
|
||||
#include "seaf-utils.h"
|
||||
|
||||
#include "seafile-session.h"
|
||||
#include "getca-proc.h"
|
||||
|
||||
/*
|
||||
seafile-putca <repo_id> <token>
|
||||
INIT -------------------------->
|
||||
OK
|
||||
<-------------------------
|
||||
|
||||
REQUEST_SENT
|
||||
commit id list
|
||||
-------------------------->
|
||||
common ancestor id
|
||||
<--------------------------
|
||||
*/
|
||||
|
||||
#define SC_ID_LIST "301"
|
||||
#define SS_ID_LIST "Commit id list"
|
||||
#define SC_ID_LIST_END "302"
|
||||
#define SS_ID_LIST_END "Commit id list end"
|
||||
#define SC_CA "303"
|
||||
#define SS_CA "Common ancestor"
|
||||
|
||||
#define SC_ACCESS_DENIED "401"
|
||||
#define SS_ACCESS_DENIED "Access denied"
|
||||
#define SC_NO_CA "404"
|
||||
#define SS_NO_CA "No common ancestor found"
|
||||
|
||||
enum {
|
||||
INIT,
|
||||
REQUEST_SENT,
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
char repo_id[41];
|
||||
char last_uploaded[41];
|
||||
char last_checkout[41];
|
||||
GList *commits;
|
||||
gboolean success;
|
||||
} SeafileGetcaProcPriv;
|
||||
|
||||
#define GET_PRIV(o) \
|
||||
(G_TYPE_INSTANCE_GET_PRIVATE ((o), SEAFILE_TYPE_GETCA_PROC, SeafileGetcaProcPriv))
|
||||
|
||||
#define USE_PRIV \
|
||||
SeafileGetcaProcPriv *priv = GET_PRIV(processor);
|
||||
|
||||
static int get_ca_start (CcnetProcessor *processor, int argc, char **argv);
|
||||
static void handle_response (CcnetProcessor *processor,
|
||||
char *code, char *code_msg,
|
||||
char *content, int clen);
|
||||
|
||||
G_DEFINE_TYPE (SeafileGetcaProc, seafile_getca_proc, CCNET_TYPE_PROCESSOR)
|
||||
|
||||
static void
|
||||
release_resource (CcnetProcessor *processor)
|
||||
{
|
||||
USE_PRIV;
|
||||
|
||||
if (priv->commits)
|
||||
string_list_free (priv->commits);
|
||||
|
||||
CCNET_PROCESSOR_CLASS (seafile_getca_proc_parent_class)->release_resource (processor);
|
||||
}
|
||||
|
||||
static void
|
||||
seafile_getca_proc_class_init (SeafileGetcaProcClass *klass)
|
||||
{
|
||||
CcnetProcessorClass *proc_class = CCNET_PROCESSOR_CLASS (klass);
|
||||
|
||||
proc_class->name = "getca-proc";
|
||||
proc_class->start = get_ca_start;
|
||||
proc_class->handle_response = handle_response;
|
||||
proc_class->release_resource = release_resource;
|
||||
|
||||
g_type_class_add_private (klass, sizeof(SeafileGetcaProcPriv));
|
||||
}
|
||||
|
||||
static void
|
||||
seafile_getca_proc_init (SeafileGetcaProc *processor)
|
||||
{
|
||||
}
|
||||
|
||||
static int
|
||||
get_ca_start (CcnetProcessor *processor, int argc, char **argv)
|
||||
{
|
||||
USE_PRIV;
|
||||
|
||||
GString *buf = g_string_new (NULL);
|
||||
|
||||
if (argc < 2) {
|
||||
ccnet_processor_done (processor, FALSE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy (priv->repo_id, argv[0], 36);
|
||||
|
||||
g_string_printf (buf, "remote %s seafile-putca %s %s",
|
||||
processor->peer_id, argv[0], argv[1]);
|
||||
ccnet_processor_send_request (processor, buf->str);
|
||||
g_string_free (buf, TRUE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
traverse_commits_cb (SeafCommit *commit, void *data, gboolean *stop)
|
||||
{
|
||||
CcnetProcessor *processor = data;
|
||||
USE_PRIV;
|
||||
|
||||
/* Add commit id to list, including last_uploaded and last checkout,
|
||||
* because they can also be the common ancestor.
|
||||
*/
|
||||
priv->commits = g_list_prepend (priv->commits, g_strdup(commit->commit_id));
|
||||
|
||||
if (strcmp (commit->commit_id, priv->last_uploaded) == 0 ||
|
||||
strcmp (commit->commit_id, priv->last_checkout) == 0) {
|
||||
*stop = TRUE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void *
|
||||
list_commits_thread (void *data)
|
||||
{
|
||||
CcnetProcessor *processor = data;
|
||||
USE_PRIV;
|
||||
char *last_uploaded, *last_checkout;
|
||||
SeafRepo *repo;
|
||||
|
||||
last_uploaded = seaf_repo_manager_get_repo_property (seaf->repo_mgr,
|
||||
priv->repo_id,
|
||||
REPO_LOCAL_HEAD);
|
||||
if (!last_uploaded) {
|
||||
seaf_warning ("Last uploaded commit id is not found in db.\n");
|
||||
priv->success = FALSE;
|
||||
return data;
|
||||
}
|
||||
memcpy (priv->last_uploaded, last_uploaded, 40);
|
||||
g_free (last_uploaded);
|
||||
|
||||
last_checkout = seaf_repo_manager_get_repo_property (seaf->repo_mgr,
|
||||
priv->repo_id,
|
||||
REPO_REMOTE_HEAD);
|
||||
if (!last_checkout) {
|
||||
seaf_warning ("Last checkout commit id is not found in db.\n");
|
||||
priv->success = FALSE;
|
||||
return data;
|
||||
}
|
||||
memcpy (priv->last_checkout, last_checkout, 40);
|
||||
g_free (last_checkout);
|
||||
|
||||
repo = seaf_repo_manager_get_repo (seaf->repo_mgr, priv->repo_id);
|
||||
if (!repo) {
|
||||
seaf_warning ("Failed to find repo %s.\n", priv->repo_id);
|
||||
priv->success = FALSE;
|
||||
return data;
|
||||
}
|
||||
|
||||
/* Since we don't download all commits, some commits may be missing.
|
||||
* But those missing commits (and their ancestors) can't be the common ancestor.
|
||||
*/
|
||||
priv->success =
|
||||
seaf_commit_manager_traverse_commit_tree_truncated (seaf->commit_mgr,
|
||||
repo->id,
|
||||
repo->version,
|
||||
repo->head->commit_id,
|
||||
traverse_commits_cb,
|
||||
processor, FALSE);
|
||||
return data;
|
||||
}
|
||||
|
||||
#define MAX_BUFFER_IDS 500
|
||||
|
||||
static void
|
||||
list_commits_done (void *vdata)
|
||||
{
|
||||
CcnetProcessor *processor = vdata;
|
||||
USE_PRIV;
|
||||
GString *buf;
|
||||
|
||||
if (!priv->success) {
|
||||
ccnet_processor_done (processor, FALSE);
|
||||
return;
|
||||
}
|
||||
|
||||
buf = g_string_new ("");
|
||||
GList *ptr;
|
||||
char *id;
|
||||
int n = 0;
|
||||
|
||||
priv->commits = g_list_reverse (priv->commits);
|
||||
for (ptr = priv->commits; ptr; ptr = ptr->next) {
|
||||
id = ptr->data;
|
||||
g_string_append (buf, id);
|
||||
++n;
|
||||
|
||||
if (n == MAX_BUFFER_IDS) {
|
||||
seaf_debug ("Sending %d commit ids.\n", n);
|
||||
ccnet_processor_send_update (processor, SC_ID_LIST, SS_ID_LIST,
|
||||
buf->str, buf->len + 1);
|
||||
n = 0;
|
||||
g_string_free (buf, TRUE);
|
||||
buf = g_string_new ("");
|
||||
}
|
||||
}
|
||||
|
||||
if (n != 0) {
|
||||
seaf_debug ("Sending %d commit ids.\n", n);
|
||||
ccnet_processor_send_update (processor, SC_ID_LIST, SS_ID_LIST,
|
||||
buf->str, buf->len + 1);
|
||||
}
|
||||
|
||||
ccnet_processor_send_update (processor, SC_ID_LIST_END, SS_ID_LIST_END,
|
||||
NULL, 0);
|
||||
|
||||
g_string_free (buf, TRUE);
|
||||
|
||||
string_list_free (priv->commits);
|
||||
priv->commits = NULL;
|
||||
|
||||
processor->state = REQUEST_SENT;
|
||||
}
|
||||
|
||||
static void
|
||||
send_commit_id_list (CcnetProcessor *processor)
|
||||
{
|
||||
ccnet_processor_thread_create (processor, seaf->job_mgr,
|
||||
list_commits_thread, list_commits_done,
|
||||
processor);
|
||||
}
|
||||
|
||||
static void handle_response (CcnetProcessor *processor,
|
||||
char *code, char *code_msg,
|
||||
char *content, int clen)
|
||||
{
|
||||
SeafileGetcaProc *proc = (SeafileGetcaProc *)processor;
|
||||
USE_PRIV;
|
||||
|
||||
switch (processor->state) {
|
||||
case INIT:
|
||||
if (strncmp(code, SC_OK, 3) == 0) {
|
||||
send_commit_id_list (processor);
|
||||
return;
|
||||
} else if (strncmp (code, SC_ACCESS_DENIED, 3) == 0) {
|
||||
seaf_warning ("Access denied to repo %.8s.\n", priv->repo_id);
|
||||
processor->failure = GETCA_PROC_ACCESS_DENIED;
|
||||
ccnet_processor_done (processor, FALSE);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case REQUEST_SENT:
|
||||
if (strncmp (code, SC_CA, 3) == 0) {
|
||||
if (clen != 41) {
|
||||
seaf_warning ("Bad common ancestor id len %d.\n", clen);
|
||||
ccnet_processor_send_update (processor, SC_SHUTDOWN, SS_SHUTDOWN,
|
||||
NULL, 0);
|
||||
ccnet_processor_done (processor, FALSE);
|
||||
}
|
||||
memcpy (proc->ca_id, content, 40);
|
||||
ccnet_processor_done (processor, TRUE);
|
||||
return;
|
||||
} else if (strncmp (code, SC_NO_CA, 3) == 0) {
|
||||
seaf_warning ("No common ancestor found for repo %.8s.\n", priv->repo_id);
|
||||
processor->failure = GETCA_PROC_NO_CA;
|
||||
ccnet_processor_done (processor, FALSE);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
g_return_if_reached ();
|
||||
}
|
||||
|
||||
g_warning ("Bad response: %s %s.\n", code, code_msg);
|
||||
ccnet_processor_done (processor, FALSE);
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#ifndef SEAFILE_GETCA_PROC_H
|
||||
#define SEAFILE_GETCA_PROC_H
|
||||
|
||||
#include <glib-object.h>
|
||||
|
||||
|
||||
#define SEAFILE_TYPE_GETCA_PROC (seafile_getca_proc_get_type ())
|
||||
#define SEAFILE_GETCA_PROC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SEAFILE_TYPE_GETCA_PROC, SeafileGetcaProc))
|
||||
#define SEAFILE_IS_GETCA_PROC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SEAFILE_TYPE_GETCA_PROC))
|
||||
#define SEAFILE_GETCA_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SEAFILE_TYPE_GETCA_PROC, SeafileGetcaProcClass))
|
||||
#define IS_SEAFILE_GETCA_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SEAFILE_TYPE_GETCA_PROC))
|
||||
#define SEAFILE_GETCA_PROC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SEAFILE_TYPE_GETCA_PROC, SeafileGetcaProcClass))
|
||||
|
||||
typedef struct _SeafileGetcaProc SeafileGetcaProc;
|
||||
typedef struct _SeafileGetcaProcClass SeafileGetcaProcClass;
|
||||
|
||||
/* Error code used in processor->failure */
|
||||
#define GETCA_PROC_ACCESS_DENIED 401
|
||||
#define GETCA_PROC_NO_CA 404
|
||||
|
||||
struct _SeafileGetcaProc {
|
||||
CcnetProcessor parent_instance;
|
||||
|
||||
char ca_id[41];
|
||||
};
|
||||
|
||||
struct _SeafileGetcaProcClass {
|
||||
CcnetProcessorClass parent_class;
|
||||
};
|
||||
|
||||
GType seafile_getca_proc_get_type ();
|
||||
|
||||
#endif
|
@ -1,250 +0,0 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#define DEBUG_FLAG SEAFILE_DEBUG_TRANSFER
|
||||
#include "log.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <ccnet.h>
|
||||
#include "net.h"
|
||||
#include "utils.h"
|
||||
#include "seaf-utils.h"
|
||||
|
||||
#include "seafile-session.h"
|
||||
#include "getcommit-v2-proc.h"
|
||||
#include "processors/objecttx-common.h"
|
||||
|
||||
/*
|
||||
seafile-putcommit-v2 <HEAD> [END] (END is empty in clone)
|
||||
INIT -------------------------->
|
||||
OK
|
||||
<-------------------------
|
||||
|
||||
Object
|
||||
FETCH_OBJ <-------------------------
|
||||
|
||||
...
|
||||
|
||||
End
|
||||
FETCH_OBJ <--------------------------
|
||||
*/
|
||||
|
||||
enum {
|
||||
INIT,
|
||||
RECV_OBJECT
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
guint32 writer_id;
|
||||
gboolean recv_ended;
|
||||
int pending_writes;
|
||||
} SeafileGetcommitV2ProcPriv;
|
||||
|
||||
#define GET_PRIV(o) \
|
||||
(G_TYPE_INSTANCE_GET_PRIVATE ((o), SEAFILE_TYPE_GETCOMMIT_V2_PROC, SeafileGetcommitV2ProcPriv))
|
||||
|
||||
#define USE_PRIV \
|
||||
SeafileGetcommitV2ProcPriv *priv = GET_PRIV(processor);
|
||||
|
||||
static int get_commit_start (CcnetProcessor *processor, int argc, char **argv);
|
||||
static void handle_response (CcnetProcessor *processor,
|
||||
char *code, char *code_msg,
|
||||
char *content, int clen);
|
||||
|
||||
G_DEFINE_TYPE (SeafileGetcommitV2Proc, seafile_getcommit_v2_proc, CCNET_TYPE_PROCESSOR)
|
||||
|
||||
static void
|
||||
release_resource (CcnetProcessor *processor)
|
||||
{
|
||||
USE_PRIV;
|
||||
|
||||
seaf_obj_store_unregister_async_write (seaf->commit_mgr->obj_store,
|
||||
priv->writer_id);
|
||||
|
||||
CCNET_PROCESSOR_CLASS (seafile_getcommit_v2_proc_parent_class)->release_resource (processor);
|
||||
}
|
||||
|
||||
static void
|
||||
seafile_getcommit_v2_proc_class_init (SeafileGetcommitV2ProcClass *klass)
|
||||
{
|
||||
CcnetProcessorClass *proc_class = CCNET_PROCESSOR_CLASS (klass);
|
||||
|
||||
proc_class->name = "getcommit-proc-v2";
|
||||
proc_class->start = get_commit_start;
|
||||
proc_class->handle_response = handle_response;
|
||||
proc_class->release_resource = release_resource;
|
||||
|
||||
g_type_class_add_private (klass, sizeof(SeafileGetcommitV2ProcPriv));
|
||||
}
|
||||
|
||||
static void
|
||||
seafile_getcommit_v2_proc_init (SeafileGetcommitV2Proc *processor)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
commit_write_cb (OSAsyncResult *res, void *data);
|
||||
|
||||
static int
|
||||
get_commit_start (CcnetProcessor *processor, int argc, char **argv)
|
||||
{
|
||||
USE_PRIV;
|
||||
GString *buf = g_string_new (NULL);
|
||||
TransferTask *task = ((SeafileGetcommitV2Proc *)processor)->tx_task;
|
||||
SeafBranch *master = NULL;
|
||||
char *end_commit_id = NULL;
|
||||
|
||||
g_return_val_if_fail (task->session_token, -1);
|
||||
|
||||
if (!task->is_clone) {
|
||||
master = seaf_branch_manager_get_branch (seaf->branch_mgr,
|
||||
task->repo_id,
|
||||
"master");
|
||||
if (master != NULL)
|
||||
end_commit_id = master->commit_id;
|
||||
}
|
||||
|
||||
/* fs_roots can be non-NULL if transfer is resumed from NET_DOWN. */
|
||||
if (task->fs_roots != NULL)
|
||||
object_list_free (task->fs_roots);
|
||||
task->fs_roots = object_list_new ();
|
||||
|
||||
priv->writer_id = seaf_obj_store_register_async_write (seaf->commit_mgr->obj_store,
|
||||
task->repo_id,
|
||||
task->repo_version,
|
||||
commit_write_cb, processor);
|
||||
|
||||
if (end_commit_id != NULL)
|
||||
g_string_printf (buf, "remote %s seafile-putcommit-v2 %s %s %s",
|
||||
processor->peer_id,
|
||||
task->head, end_commit_id, task->session_token);
|
||||
else
|
||||
g_string_printf (buf, "remote %s seafile-putcommit-v2 %s %s",
|
||||
processor->peer_id,
|
||||
task->head, task->session_token);
|
||||
ccnet_processor_send_request (processor, buf->str);
|
||||
g_string_free (buf, TRUE);
|
||||
|
||||
seaf_branch_unref (master);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
commit_write_cb (OSAsyncResult *res, void *data)
|
||||
{
|
||||
CcnetProcessor *processor = data;
|
||||
USE_PRIV;
|
||||
TransferTask *task = ((SeafileGetcommitV2Proc *)processor)->tx_task;
|
||||
SeafCommit *commit;
|
||||
|
||||
if (!res->success) {
|
||||
seaf_warning ("Failed to write commit %.8s.\n", res->obj_id);
|
||||
transfer_task_set_error (task, TASK_ERR_DOWNLOAD_COMMIT);
|
||||
ccnet_processor_send_update (processor, SC_SHUTDOWN, SS_SHUTDOWN, NULL, 0);
|
||||
ccnet_processor_done (processor, FALSE);
|
||||
return;
|
||||
}
|
||||
|
||||
commit = seaf_commit_from_data (res->obj_id, res->data, res->len);
|
||||
if (!commit) {
|
||||
seaf_warning ("[getcommit] Bad commit object received.\n");
|
||||
transfer_task_set_error (task, TASK_ERR_DOWNLOAD_COMMIT);
|
||||
ccnet_processor_send_update (processor, SC_BAD_OBJECT, SS_BAD_OBJECT,
|
||||
NULL, 0);
|
||||
ccnet_processor_done (processor, FALSE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcmp (commit->root_id, EMPTY_SHA1) != 0)
|
||||
object_list_insert (task->fs_roots, commit->root_id);
|
||||
seaf_commit_unref (commit);
|
||||
|
||||
if (--(priv->pending_writes) == 0 && priv->recv_ended)
|
||||
ccnet_processor_done (processor, TRUE);
|
||||
}
|
||||
|
||||
static int
|
||||
save_commit (CcnetProcessor *processor, ObjectPack *pack, int len)
|
||||
{
|
||||
USE_PRIV;
|
||||
|
||||
int rc = seaf_obj_store_async_write (seaf->commit_mgr->obj_store,
|
||||
priv->writer_id,
|
||||
pack->id,
|
||||
pack->object,
|
||||
len - 41,
|
||||
FALSE);
|
||||
++(priv->pending_writes);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void
|
||||
receive_commit (CcnetProcessor *processor, char *content, int clen)
|
||||
{
|
||||
ObjectPack *pack = (ObjectPack *)content;
|
||||
|
||||
if (clen < sizeof(ObjectPack)) {
|
||||
seaf_warning ("[getcommit] invalid object id.\n");
|
||||
goto bad;
|
||||
}
|
||||
|
||||
seaf_debug ("[getcommit] recv commit object %.8s\n", pack->id);
|
||||
|
||||
if (save_commit (processor, pack, clen) < 0) {
|
||||
goto bad;
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
bad:
|
||||
seaf_warning ("[getcommit] Bad commit object received.\n");
|
||||
transfer_task_set_error (((SeafileGetcommitV2Proc *)processor)->tx_task,
|
||||
TASK_ERR_DOWNLOAD_COMMIT);
|
||||
ccnet_processor_send_update (processor, SC_BAD_OBJECT, SS_BAD_OBJECT,
|
||||
NULL, 0);
|
||||
ccnet_processor_done (processor, FALSE);
|
||||
}
|
||||
|
||||
static void handle_response (CcnetProcessor *processor,
|
||||
char *code, char *code_msg,
|
||||
char *content, int clen)
|
||||
{
|
||||
SeafileGetcommitV2Proc *proc = (SeafileGetcommitV2Proc *)processor;
|
||||
USE_PRIV;
|
||||
|
||||
if (proc->tx_task->state != TASK_STATE_NORMAL) {
|
||||
ccnet_processor_done (processor, TRUE);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (processor->state) {
|
||||
case INIT:
|
||||
if (strncmp(code, SC_OK, 3) == 0) {
|
||||
processor->state = RECV_OBJECT;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case RECV_OBJECT:
|
||||
if (strncmp(code, SC_OBJECT, 3) == 0) {
|
||||
receive_commit (processor, content, clen);
|
||||
return;
|
||||
} else if (strncmp (code, SC_END, 3) == 0) {
|
||||
seaf_debug ("[getcommit] Get commit end.\n");
|
||||
priv->recv_ended = TRUE;
|
||||
if (priv->pending_writes == 0)
|
||||
ccnet_processor_done (processor, TRUE);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
g_return_if_reached ();
|
||||
}
|
||||
|
||||
seaf_warning ("Bad response: %s %s.\n", code, code_msg);
|
||||
if (memcmp (code, SC_ACCESS_DENIED, 3) == 0)
|
||||
transfer_task_set_error (proc->tx_task, TASK_ERR_ACCESS_DENIED);
|
||||
ccnet_processor_done (processor, FALSE);
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#ifndef SEAFILE_GETCOMMIT_V2_PROC_H
|
||||
#define SEAFILE_GETCOMMIT_V2_PROC_H
|
||||
|
||||
#include <glib-object.h>
|
||||
|
||||
|
||||
#define SEAFILE_TYPE_GETCOMMIT_V2_PROC (seafile_getcommit_v2_proc_get_type ())
|
||||
#define SEAFILE_GETCOMMIT_V2_PROC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SEAFILE_TYPE_GETCOMMIT_V2_PROC, SeafileGetcommitV2Proc))
|
||||
#define SEAFILE_IS_GETCOMMIT_V2_PROC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SEAFILE_TYPE_GETCOMMIT_V2_PROC))
|
||||
#define SEAFILE_GETCOMMIT_V2_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SEAFILE_TYPE_GETCOMMIT_V2_PROC, SeafileGetcommitV2ProcClass))
|
||||
#define IS_SEAFILE_GETCOMMIT_V2_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SEAFILE_TYPE_GETCOMMIT_V2_PROC))
|
||||
#define SEAFILE_GETCOMMIT_V2_PROC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SEAFILE_TYPE_GETCOMMIT_V2_PROC, SeafileGetcommitV2ProcClass))
|
||||
|
||||
typedef struct _SeafileGetcommitV2Proc SeafileGetcommitV2Proc;
|
||||
typedef struct _SeafileGetcommitV2ProcClass SeafileGetcommitV2ProcClass;
|
||||
|
||||
struct _SeafileGetcommitV2Proc {
|
||||
CcnetProcessor parent_instance;
|
||||
|
||||
TransferTask *tx_task;
|
||||
};
|
||||
|
||||
struct _SeafileGetcommitV2ProcClass {
|
||||
CcnetProcessorClass parent_class;
|
||||
};
|
||||
|
||||
GType seafile_getcommit_v2_proc_get_type ();
|
||||
|
||||
#endif
|
@ -1,201 +0,0 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#define DEBUG_FLAG SEAFILE_DEBUG_TRANSFER
|
||||
#include "log.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <ccnet.h>
|
||||
#include "net.h"
|
||||
#include "utils.h"
|
||||
#include "seaf-utils.h"
|
||||
|
||||
#include "seafile-session.h"
|
||||
#include "getcommit-v3-proc.h"
|
||||
#include "processors/objecttx-common.h"
|
||||
|
||||
/*
|
||||
seafile-putcommit-v3 <HEAD>
|
||||
INIT -------------------------->
|
||||
|
||||
Object
|
||||
<-------------------------
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
guint32 writer_id;
|
||||
} SeafileGetcommitV3ProcPriv;
|
||||
|
||||
#define GET_PRIV(o) \
|
||||
(G_TYPE_INSTANCE_GET_PRIVATE ((o), SEAFILE_TYPE_GETCOMMIT_V3_PROC, SeafileGetcommitV3ProcPriv))
|
||||
|
||||
#define USE_PRIV \
|
||||
SeafileGetcommitV3ProcPriv *priv = GET_PRIV(processor);
|
||||
|
||||
static int get_commit_start (CcnetProcessor *processor, int argc, char **argv);
|
||||
static void handle_response (CcnetProcessor *processor,
|
||||
char *code, char *code_msg,
|
||||
char *content, int clen);
|
||||
|
||||
G_DEFINE_TYPE (SeafileGetcommitV3Proc, seafile_getcommit_v3_proc, CCNET_TYPE_PROCESSOR)
|
||||
|
||||
static void
|
||||
release_resource (CcnetProcessor *processor)
|
||||
{
|
||||
USE_PRIV;
|
||||
|
||||
seaf_obj_store_unregister_async_write (seaf->commit_mgr->obj_store,
|
||||
priv->writer_id);
|
||||
|
||||
CCNET_PROCESSOR_CLASS (seafile_getcommit_v3_proc_parent_class)->release_resource (processor);
|
||||
}
|
||||
|
||||
static void
|
||||
seafile_getcommit_v3_proc_class_init (SeafileGetcommitV3ProcClass *klass)
|
||||
{
|
||||
CcnetProcessorClass *proc_class = CCNET_PROCESSOR_CLASS (klass);
|
||||
|
||||
proc_class->name = "getcommit-proc-v3";
|
||||
proc_class->start = get_commit_start;
|
||||
proc_class->handle_response = handle_response;
|
||||
proc_class->release_resource = release_resource;
|
||||
|
||||
g_type_class_add_private (klass, sizeof(SeafileGetcommitV3ProcPriv));
|
||||
}
|
||||
|
||||
static void
|
||||
seafile_getcommit_v3_proc_init (SeafileGetcommitV3Proc *processor)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
commit_write_cb (OSAsyncResult *res, void *data);
|
||||
|
||||
static int
|
||||
get_commit_start (CcnetProcessor *processor, int argc, char **argv)
|
||||
{
|
||||
USE_PRIV;
|
||||
GString *buf = g_string_new (NULL);
|
||||
TransferTask *task = ((SeafileGetcommitV3Proc *)processor)->tx_task;
|
||||
SeafBranch *master = NULL;
|
||||
|
||||
g_return_val_if_fail (task->session_token, -1);
|
||||
|
||||
/* fs_roots can be non-NULL if transfer is resumed from NET_DOWN. */
|
||||
if (task->fs_roots != NULL)
|
||||
object_list_free (task->fs_roots);
|
||||
task->fs_roots = object_list_new ();
|
||||
|
||||
priv->writer_id = seaf_obj_store_register_async_write (seaf->commit_mgr->obj_store,
|
||||
task->repo_id,
|
||||
task->repo_version,
|
||||
commit_write_cb, processor);
|
||||
|
||||
g_string_printf (buf, "remote %s seafile-putcommit-v3 %s %s",
|
||||
processor->peer_id,
|
||||
task->head, task->session_token);
|
||||
|
||||
ccnet_processor_send_request (processor, buf->str);
|
||||
g_string_free (buf, TRUE);
|
||||
|
||||
seaf_branch_unref (master);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
commit_write_cb (OSAsyncResult *res, void *data)
|
||||
{
|
||||
CcnetProcessor *processor = data;
|
||||
TransferTask *task = ((SeafileGetcommitV3Proc *)processor)->tx_task;
|
||||
SeafCommit *commit;
|
||||
|
||||
if (!res->success) {
|
||||
seaf_warning ("Failed to write commit %.8s.\n", res->obj_id);
|
||||
transfer_task_set_error (task, TASK_ERR_DOWNLOAD_COMMIT);
|
||||
ccnet_processor_send_update (processor, SC_SHUTDOWN, SS_SHUTDOWN, NULL, 0);
|
||||
ccnet_processor_done (processor, FALSE);
|
||||
return;
|
||||
}
|
||||
|
||||
commit = seaf_commit_from_data (res->obj_id, res->data, res->len);
|
||||
if (!commit) {
|
||||
seaf_warning ("[getcommit] Bad commit object received.\n");
|
||||
transfer_task_set_error (task, TASK_ERR_DOWNLOAD_COMMIT);
|
||||
ccnet_processor_send_update (processor, SC_BAD_OBJECT, SS_BAD_OBJECT,
|
||||
NULL, 0);
|
||||
ccnet_processor_done (processor, FALSE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcmp (commit->root_id, EMPTY_SHA1) != 0)
|
||||
object_list_insert (task->fs_roots, commit->root_id);
|
||||
seaf_commit_unref (commit);
|
||||
|
||||
ccnet_processor_done (processor, TRUE);
|
||||
}
|
||||
|
||||
static int
|
||||
save_commit (CcnetProcessor *processor, ObjectPack *pack, int len)
|
||||
{
|
||||
USE_PRIV;
|
||||
|
||||
int rc = seaf_obj_store_async_write (seaf->commit_mgr->obj_store,
|
||||
priv->writer_id,
|
||||
pack->id,
|
||||
pack->object,
|
||||
len - 41,
|
||||
FALSE);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void
|
||||
receive_commit (CcnetProcessor *processor, char *content, int clen)
|
||||
{
|
||||
ObjectPack *pack = (ObjectPack *)content;
|
||||
|
||||
if (clen < sizeof(ObjectPack)) {
|
||||
seaf_warning ("[getcommit] invalid object id.\n");
|
||||
goto bad;
|
||||
}
|
||||
|
||||
seaf_debug ("[getcommit] recv commit object %.8s\n", pack->id);
|
||||
|
||||
if (save_commit (processor, pack, clen) < 0) {
|
||||
goto bad;
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
bad:
|
||||
seaf_warning ("[getcommit] Bad commit object received.\n");
|
||||
transfer_task_set_error (((SeafileGetcommitV3Proc *)processor)->tx_task,
|
||||
TASK_ERR_DOWNLOAD_COMMIT);
|
||||
ccnet_processor_send_update (processor, SC_BAD_OBJECT, SS_BAD_OBJECT,
|
||||
NULL, 0);
|
||||
ccnet_processor_done (processor, FALSE);
|
||||
}
|
||||
|
||||
static void handle_response (CcnetProcessor *processor,
|
||||
char *code, char *code_msg,
|
||||
char *content, int clen)
|
||||
{
|
||||
SeafileGetcommitV3Proc *proc = (SeafileGetcommitV3Proc *)processor;
|
||||
|
||||
if (proc->tx_task->state != TASK_STATE_NORMAL) {
|
||||
ccnet_processor_done (processor, TRUE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (strncmp(code, SC_OK, 3) == 0) {
|
||||
receive_commit (processor, content, clen);
|
||||
return;
|
||||
}
|
||||
|
||||
seaf_warning ("Bad response: %s %s.\n", code, code_msg);
|
||||
if (memcmp (code, SC_ACCESS_DENIED, 3) == 0)
|
||||
transfer_task_set_error (proc->tx_task, TASK_ERR_ACCESS_DENIED);
|
||||
ccnet_processor_done (processor, FALSE);
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#ifndef SEAFILE_GETCOMMIT_V3_PROC_H
|
||||
#define SEAFILE_GETCOMMIT_V3_PROC_H
|
||||
|
||||
#include <glib-object.h>
|
||||
|
||||
|
||||
#define SEAFILE_TYPE_GETCOMMIT_V3_PROC (seafile_getcommit_v3_proc_get_type ())
|
||||
#define SEAFILE_GETCOMMIT_V3_PROC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SEAFILE_TYPE_GETCOMMIT_V3_PROC, SeafileGetcommitV3Proc))
|
||||
#define SEAFILE_IS_GETCOMMIT_V3_PROC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SEAFILE_TYPE_GETCOMMIT_V3_PROC))
|
||||
#define SEAFILE_GETCOMMIT_V3_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SEAFILE_TYPE_GETCOMMIT_V3_PROC, SeafileGetcommitV3ProcClass))
|
||||
#define IS_SEAFILE_GETCOMMIT_V3_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SEAFILE_TYPE_GETCOMMIT_V3_PROC))
|
||||
#define SEAFILE_GETCOMMIT_V3_PROC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SEAFILE_TYPE_GETCOMMIT_V3_PROC, SeafileGetcommitV3ProcClass))
|
||||
|
||||
typedef struct _SeafileGetcommitV3Proc SeafileGetcommitV3Proc;
|
||||
typedef struct _SeafileGetcommitV3ProcClass SeafileGetcommitV3ProcClass;
|
||||
|
||||
struct _SeafileGetcommitV3Proc {
|
||||
CcnetProcessor parent_instance;
|
||||
|
||||
TransferTask *tx_task;
|
||||
};
|
||||
|
||||
struct _SeafileGetcommitV3ProcClass {
|
||||
CcnetProcessorClass parent_class;
|
||||
};
|
||||
|
||||
GType seafile_getcommit_v3_proc_get_type ();
|
||||
|
||||
#endif
|
@ -1,143 +0,0 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <glib.h>
|
||||
|
||||
#include <ccnet.h>
|
||||
|
||||
#include "seafile-session.h"
|
||||
#include "utils.h"
|
||||
#include "db.h"
|
||||
#include "getcs-proc.h"
|
||||
|
||||
#include "log.h"
|
||||
|
||||
G_DEFINE_TYPE (SeafileGetcsProc, seafile_getcs_proc, CCNET_TYPE_PROCESSOR)
|
||||
|
||||
static int start (CcnetProcessor *processor, int argc, char **argv);
|
||||
static void handle_response (CcnetProcessor *processor,
|
||||
char *code, char *code_msg,
|
||||
char *content, int clen);
|
||||
|
||||
static void
|
||||
release_resource(CcnetProcessor *processor)
|
||||
{
|
||||
CCNET_PROCESSOR_CLASS (seafile_getcs_proc_parent_class)->release_resource (processor);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
seafile_getcs_proc_class_init (SeafileGetcsProcClass *klass)
|
||||
{
|
||||
CcnetProcessorClass *proc_class = CCNET_PROCESSOR_CLASS (klass);
|
||||
|
||||
proc_class->name = "getcs-proc";
|
||||
proc_class->start = start;
|
||||
proc_class->handle_response = handle_response;
|
||||
proc_class->release_resource = release_resource;
|
||||
}
|
||||
|
||||
static void
|
||||
seafile_getcs_proc_init (SeafileGetcsProc *processor)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
start (CcnetProcessor *processor, int argc, char **argv)
|
||||
{
|
||||
char buf[256];
|
||||
|
||||
snprintf (buf, 256, "remote %s seafile-putcs", processor->peer_id);
|
||||
ccnet_processor_send_request (processor, buf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static gint
|
||||
peer_cmp_func (gconstpointer a, gconstpointer b)
|
||||
{
|
||||
const char *id_a = a;
|
||||
const char *id_b = b;
|
||||
|
||||
return (g_strcmp0(id_a, id_b));
|
||||
}
|
||||
|
||||
static void
|
||||
add_chunk_server (CcnetProcessor *processor, TransferTask *task, char *cs_str)
|
||||
{
|
||||
int num;
|
||||
char *cs_id;
|
||||
char **tokens;
|
||||
|
||||
tokens = strsplit_by_space (cs_str, &num);
|
||||
if (num < 1)
|
||||
return;
|
||||
cs_id = tokens[0];
|
||||
|
||||
if (g_list_find_custom (task->chunk_servers, cs_id, peer_cmp_func) != NULL)
|
||||
goto out;
|
||||
|
||||
if (strcmp (cs_id, processor->peer_id) == 0) {
|
||||
CcnetPeer *peer = ccnet_get_peer (seaf->ccnetrpc_client,
|
||||
processor->peer_id);
|
||||
g_return_if_fail (peer != NULL);
|
||||
if (!peer->public_addr) {
|
||||
seaf_warning ("Public address of relay %s is not set.\n", cs_id);
|
||||
g_object_unref (peer);
|
||||
goto out;
|
||||
}
|
||||
task->chunk_servers = g_list_prepend (task->chunk_servers,
|
||||
g_strdup(cs_id));
|
||||
g_object_unref (peer);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ccnet_add_peer (processor->session, tokens[0], tokens[1]);
|
||||
task->chunk_servers = g_list_prepend (task->chunk_servers, g_strdup(cs_id));
|
||||
|
||||
out:
|
||||
free (tokens);
|
||||
}
|
||||
|
||||
static void
|
||||
handle_response (CcnetProcessor *processor,
|
||||
char *code, char *code_msg,
|
||||
char *content, int clen)
|
||||
{
|
||||
SeafileGetcsProc *proc = (SeafileGetcsProc *)processor;
|
||||
char *cs_str;
|
||||
|
||||
if (proc->task->state != TASK_STATE_NORMAL) {
|
||||
g_debug ("Task not running, get-cs proc exits.\n");
|
||||
ccnet_processor_done (processor, FALSE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (memcmp (code, SC_OK, 3) != 0) {
|
||||
seaf_warning ("Bad response: %s %s.\n", code, code_msg);
|
||||
ccnet_processor_done (processor, FALSE);
|
||||
return;
|
||||
}
|
||||
if (content[clen-1] != '\0') {
|
||||
seaf_warning ("Bad chunk server list format.\n");
|
||||
ccnet_processor_done (processor, FALSE);
|
||||
return;
|
||||
}
|
||||
|
||||
cs_str = strtok (content, "\n");
|
||||
if (cs_str != NULL) {
|
||||
add_chunk_server (processor, proc->task, cs_str);
|
||||
} else {
|
||||
ccnet_processor_done (processor, TRUE);
|
||||
return;
|
||||
}
|
||||
|
||||
while ((cs_str = strtok(NULL, "\n")) != NULL)
|
||||
add_chunk_server (processor, proc->task, cs_str);
|
||||
|
||||
ccnet_processor_done (processor, TRUE);
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#ifndef SEAFILE_GETCS_PROC_H
|
||||
#define SEAFILE_GETCS_PROC_H
|
||||
|
||||
#include <glib-object.h>
|
||||
#include <ccnet/processor.h>
|
||||
#include "transfer-mgr.h"
|
||||
|
||||
#define SEAFILE_TYPE_GETCS_PROC (seafile_getcs_proc_get_type ())
|
||||
#define SEAFILE_GETCS_PROC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SEAFILE_TYPE_GETCS_PROC, SeafileGetcsProc))
|
||||
#define SEAFILE_IS_GETCS_PROC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SEAFILE_TYPE_GETCS_PROC))
|
||||
#define SEAFILE_GETCS_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SEAFILE_TYPE_GETCS_PROC, SeafileGetcsProcClass))
|
||||
#define IS_SEAFILE_GETCS_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SEAFILE_TYPE_GETCS_PROC))
|
||||
#define SEAFILE_GETCS_PROC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SEAFILE_TYPE_GETCS_PROC, SeafileGetcsProcClass))
|
||||
|
||||
typedef struct _SeafileGetcsProc SeafileGetcsProc;
|
||||
typedef struct _SeafileGetcsProcClass SeafileGetcsProcClass;
|
||||
|
||||
struct _SeafileGetcsProc {
|
||||
CcnetProcessor parent_instance;
|
||||
|
||||
TransferTask *task;
|
||||
};
|
||||
|
||||
struct _SeafileGetcsProcClass {
|
||||
CcnetProcessorClass parent_class;
|
||||
};
|
||||
|
||||
GType seafile_getcs_proc_get_type ();
|
||||
|
||||
#endif
|
||||
|
@ -1,142 +0,0 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <glib.h>
|
||||
|
||||
#include <ccnet.h>
|
||||
|
||||
#include "seafile-session.h"
|
||||
#include "utils.h"
|
||||
#include "getcs-v2-proc.h"
|
||||
|
||||
#include "log.h"
|
||||
|
||||
G_DEFINE_TYPE (SeafileGetcsV2Proc, seafile_getcs_v2_proc, CCNET_TYPE_PROCESSOR)
|
||||
|
||||
static int start (CcnetProcessor *processor, int argc, char **argv);
|
||||
static void handle_response (CcnetProcessor *processor,
|
||||
char *code, char *code_msg,
|
||||
char *content, int clen);
|
||||
|
||||
static void
|
||||
release_resource(CcnetProcessor *processor)
|
||||
{
|
||||
CCNET_PROCESSOR_CLASS (seafile_getcs_v2_proc_parent_class)->release_resource (processor);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
seafile_getcs_v2_proc_class_init (SeafileGetcsV2ProcClass *klass)
|
||||
{
|
||||
CcnetProcessorClass *proc_class = CCNET_PROCESSOR_CLASS (klass);
|
||||
|
||||
proc_class->name = "getcs-v2-proc";
|
||||
proc_class->start = start;
|
||||
proc_class->handle_response = handle_response;
|
||||
proc_class->release_resource = release_resource;
|
||||
}
|
||||
|
||||
static void
|
||||
seafile_getcs_v2_proc_init (SeafileGetcsV2Proc *processor)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
start (CcnetProcessor *processor, int argc, char **argv)
|
||||
{
|
||||
char buf[256];
|
||||
|
||||
snprintf (buf, 256, "remote %s seafile-putcs-v2", processor->peer_id);
|
||||
ccnet_processor_send_request (processor, buf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
add_chunk_server (CcnetProcessor *processor, TransferTask *task, char *cs_str)
|
||||
{
|
||||
char **tokens;
|
||||
CcnetPeer *peer;
|
||||
ChunkServer *cs;
|
||||
|
||||
tokens = g_strsplit (cs_str, ":", -1);
|
||||
if (g_strv_length (tokens) != 2) {
|
||||
seaf_warning ("Invalid chunk server address format: %s.\n", cs_str);
|
||||
g_strfreev (tokens);
|
||||
return -1;
|
||||
}
|
||||
|
||||
peer = ccnet_get_peer (seaf->ccnetrpc_client, processor->peer_id);
|
||||
if (!peer) {
|
||||
seaf_warning ("[getcs] Invalid peer %s.\n", processor->peer_id);
|
||||
g_strfreev (tokens);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!peer->addr_str) {
|
||||
seaf_warning ("[getcs] Peer doesn't have an address.\n");
|
||||
g_object_unref (peer);
|
||||
g_strfreev (tokens);
|
||||
return -1;
|
||||
}
|
||||
|
||||
cs = g_new0 (ChunkServer, 1);
|
||||
cs->addr = g_strdup (peer->addr_str);
|
||||
cs->port = atoi (tokens[1]);
|
||||
|
||||
task->chunk_servers = g_list_prepend (task->chunk_servers, cs);
|
||||
|
||||
g_strfreev (tokens);
|
||||
g_object_unref (peer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
handle_response (CcnetProcessor *processor,
|
||||
char *code, char *code_msg,
|
||||
char *content, int clen)
|
||||
{
|
||||
SeafileGetcsV2Proc *proc = (SeafileGetcsV2Proc *)processor;
|
||||
char *cs_str;
|
||||
|
||||
if (proc->task->state != TASK_STATE_NORMAL) {
|
||||
g_debug ("Task not running, get-cs proc exits.\n");
|
||||
ccnet_processor_done (processor, FALSE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (memcmp (code, SC_OK, 3) != 0) {
|
||||
seaf_warning ("Bad response: %s %s.\n", code, code_msg);
|
||||
ccnet_processor_done (processor, FALSE);
|
||||
return;
|
||||
}
|
||||
if (content[clen-1] != '\0') {
|
||||
seaf_warning ("Bad chunk server list format.\n");
|
||||
ccnet_processor_done (processor, FALSE);
|
||||
return;
|
||||
}
|
||||
|
||||
cs_str = strtok (content, "\n");
|
||||
if (cs_str != NULL) {
|
||||
if (add_chunk_server (processor, proc->task, cs_str) < 0)
|
||||
goto error;
|
||||
} else {
|
||||
ccnet_processor_done (processor, TRUE);
|
||||
return;
|
||||
}
|
||||
|
||||
while ((cs_str = strtok(NULL, "\n")) != NULL) {
|
||||
if (add_chunk_server (processor, proc->task, cs_str) < 0)
|
||||
goto error;
|
||||
}
|
||||
|
||||
ccnet_processor_done (processor, TRUE);
|
||||
|
||||
error:
|
||||
ccnet_processor_send_update (processor, SC_SHUTDOWN, SS_SHUTDOWN, NULL, 0);
|
||||
ccnet_processor_done (processor, FALSE);
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#ifndef SEAFILE_GETCS_V2_PROC_H
|
||||
#define SEAFILE_GETCS_V2_PROC_H
|
||||
|
||||
#include <glib-object.h>
|
||||
#include <ccnet/processor.h>
|
||||
#include "transfer-mgr.h"
|
||||
|
||||
#define SEAFILE_TYPE_GETCS_V2_PROC (seafile_getcs_v2_proc_get_type ())
|
||||
#define SEAFILE_GETCS_V2_PROC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SEAFILE_TYPE_GETCS_V2_PROC, SeafileGetcsV2Proc))
|
||||
#define SEAFILE_IS_GETCS_V2_PROC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SEAFILE_TYPE_GETCS_V2_PROC))
|
||||
#define SEAFILE_GETCS_V2_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SEAFILE_TYPE_GETCS_V2_PROC, SeafileGetcsV2ProcClass))
|
||||
#define IS_SEAFILE_GETCS_V2_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SEAFILE_TYPE_GETCS_V2_PROC))
|
||||
#define SEAFILE_GETCS_V2_PROC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SEAFILE_TYPE_GETCS_V2_PROC, SeafileGetcsV2ProcClass))
|
||||
|
||||
typedef struct _SeafileGetcsV2Proc SeafileGetcsV2Proc;
|
||||
typedef struct _SeafileGetcsV2ProcClass SeafileGetcsV2ProcClass;
|
||||
|
||||
struct _SeafileGetcsV2Proc {
|
||||
CcnetProcessor parent_instance;
|
||||
|
||||
TransferTask *task;
|
||||
};
|
||||
|
||||
struct _SeafileGetcsV2ProcClass {
|
||||
CcnetProcessorClass parent_class;
|
||||
};
|
||||
|
||||
GType seafile_getcs_v2_proc_get_type ();
|
||||
|
||||
#endif
|
||||
|
@ -1,602 +0,0 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#define DEBUG_FLAG SEAFILE_DEBUG_TRANSFER
|
||||
#include "log.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <ccnet.h>
|
||||
#include "utils.h"
|
||||
#include "seaf-utils.h"
|
||||
|
||||
#include "seafile-session.h"
|
||||
#include "commit-mgr.h"
|
||||
#include "fs-mgr.h"
|
||||
#include "processors/objecttx-common.h"
|
||||
#include "getfs-proc.h"
|
||||
#include "transfer-mgr.h"
|
||||
|
||||
/*
|
||||
* Implementation Notes:
|
||||
*
|
||||
* Checking and writing of fs objects are completely asynchronous in this processor.
|
||||
* - FS object checking is done by a worker thread.
|
||||
* - Writing of received fs objects is done with the async obj-store API.
|
||||
*
|
||||
* At the beginning, all root object id is put into inspect queue. And then
|
||||
* We start a worker thread to check the first object in the inspect queue.
|
||||
*
|
||||
* After the worker thread is done, we send object requests in the main thread.
|
||||
* And then we start a worker to check the next object in the inspect queue.
|
||||
*
|
||||
* After an object is received and store asynchronously into disk, and if the object
|
||||
* is a directory, we put it into the inspect queue. And then we start a worker
|
||||
* to check the next object in the inspect queue, if no worker is running.
|
||||
* This means only 1 worker can be running at the same time. Because we use thread
|
||||
* pool, there will be no performance problem of creating threads.
|
||||
*
|
||||
* The end condition is checked after:
|
||||
* - worker thread is done
|
||||
* - an object is written
|
||||
* The end condition is
|
||||
* - inspect queue is empty, and
|
||||
* - no object request is pending, and
|
||||
* - no worker is running
|
||||
*/
|
||||
|
||||
#define MAX_NUM_BATCH 64
|
||||
|
||||
enum {
|
||||
REQUEST_SENT,
|
||||
FETCH_OBJECT
|
||||
};
|
||||
|
||||
typedef struct ThreadData {
|
||||
gint refcnt;
|
||||
CcnetProcessor *processor;
|
||||
gboolean is_clone;
|
||||
int cmd_pipe;
|
||||
uint32_t cevent_id;
|
||||
char root_id[41];
|
||||
GHashTable *fs_objects;
|
||||
GList *fetch_objs;
|
||||
char repo_id[37];
|
||||
int repo_version;
|
||||
} ThreadData;
|
||||
|
||||
typedef struct {
|
||||
gboolean worker_checking;
|
||||
gboolean worker_started;
|
||||
GQueue *inspect_queue; /* objects to check exists */
|
||||
int pending_objects;
|
||||
guint32 writer_id;
|
||||
|
||||
/* Used by worker thread */
|
||||
int cmd_pipe[2];
|
||||
uint32_t cevent_id;
|
||||
ThreadData *tdata;
|
||||
|
||||
char buf[4096];
|
||||
char *bufptr;
|
||||
int n_batch;
|
||||
|
||||
char *obj_seg;
|
||||
int obj_seg_len;
|
||||
} SeafileGetfsProcPriv;
|
||||
|
||||
#define GET_PRIV(o) \
|
||||
(G_TYPE_INSTANCE_GET_PRIVATE ((o), SEAFILE_TYPE_GETFS_PROC, SeafileGetfsProcPriv))
|
||||
|
||||
#define USE_PRIV \
|
||||
SeafileGetfsProcPriv *priv = GET_PRIV(processor);
|
||||
|
||||
|
||||
G_DEFINE_TYPE (SeafileGetfsProc, seafile_getfs_proc, CCNET_TYPE_PROCESSOR)
|
||||
|
||||
static int start (CcnetProcessor *processor, int argc, char **argv);
|
||||
static void handle_response (CcnetProcessor *processor,
|
||||
char *code, char *code_msg,
|
||||
char *content, int clen);
|
||||
|
||||
static void
|
||||
thread_data_ref (ThreadData *tdata)
|
||||
{
|
||||
g_atomic_int_inc (&tdata->refcnt);
|
||||
}
|
||||
|
||||
static void
|
||||
thread_data_unref (ThreadData *tdata)
|
||||
{
|
||||
if (g_atomic_int_dec_and_test (&tdata->refcnt)) {
|
||||
if (tdata->fetch_objs)
|
||||
string_list_free (tdata->fetch_objs);
|
||||
if (tdata->fs_objects)
|
||||
g_hash_table_destroy (tdata->fs_objects);
|
||||
g_free (tdata);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
release_resource(CcnetProcessor *processor)
|
||||
{
|
||||
USE_PRIV;
|
||||
g_queue_free (priv->inspect_queue);
|
||||
g_free (priv->obj_seg);
|
||||
seaf_obj_store_unregister_async_write (seaf->fs_mgr->obj_store, priv->writer_id);
|
||||
|
||||
if (priv->worker_started) {
|
||||
/* The worker thread will notice the command pipe has been closed and exits.
|
||||
*/
|
||||
pipeclose (priv->cmd_pipe[1]);
|
||||
cevent_manager_unregister (seaf->ev_mgr, priv->cevent_id);
|
||||
thread_data_unref (priv->tdata);
|
||||
}
|
||||
|
||||
CCNET_PROCESSOR_CLASS (seafile_getfs_proc_parent_class)->release_resource (processor);
|
||||
}
|
||||
|
||||
static void
|
||||
seafile_getfs_proc_class_init (SeafileGetfsProcClass *klass)
|
||||
{
|
||||
CcnetProcessorClass *proc_class = CCNET_PROCESSOR_CLASS (klass);
|
||||
|
||||
proc_class->name = "getfs-proc";
|
||||
proc_class->start = start;
|
||||
proc_class->handle_response = handle_response;
|
||||
proc_class->release_resource = release_resource;
|
||||
|
||||
g_type_class_add_private (klass, sizeof (SeafileGetfsProcPriv));
|
||||
}
|
||||
|
||||
static void
|
||||
seafile_getfs_proc_init (SeafileGetfsProc *processor)
|
||||
{
|
||||
}
|
||||
|
||||
inline static void
|
||||
request_object_batch_begin (SeafileGetfsProcPriv *priv)
|
||||
{
|
||||
priv->bufptr = priv->buf;
|
||||
priv->n_batch = 0;
|
||||
}
|
||||
|
||||
inline static void
|
||||
request_object_batch_flush (CcnetProcessor *processor,
|
||||
SeafileGetfsProcPriv *priv)
|
||||
{
|
||||
if (priv->bufptr == priv->buf)
|
||||
return;
|
||||
*priv->bufptr = '\0'; /* add ending '\0' */
|
||||
priv->bufptr++;
|
||||
|
||||
ccnet_processor_send_update (processor, SC_GET_OBJECT, SS_GET_OBJECT,
|
||||
priv->buf, priv->bufptr - priv->buf);
|
||||
|
||||
/* Clean state */
|
||||
priv->n_batch = 0;
|
||||
priv->bufptr = priv->buf;
|
||||
}
|
||||
|
||||
inline static void
|
||||
request_object_batch (CcnetProcessor *processor,
|
||||
SeafileGetfsProcPriv *priv,
|
||||
const char *id)
|
||||
{
|
||||
memcpy (priv->bufptr, id, 40);
|
||||
priv->bufptr += 40;
|
||||
*priv->bufptr = '\n';
|
||||
priv->bufptr++;
|
||||
|
||||
if (++priv->n_batch == MAX_NUM_BATCH)
|
||||
request_object_batch_flush (processor, priv);
|
||||
++priv->pending_objects;
|
||||
}
|
||||
|
||||
/*
|
||||
* Recursively check fs tree rooted at @dir_id. This function returns when
|
||||
* all non-existent or invalid objects have been put into data->fetch_objs.
|
||||
*/
|
||||
static void
|
||||
check_seafdir (ThreadData *tdata, const char *dir_id)
|
||||
{
|
||||
SeafDir *dir = NULL;
|
||||
GList *ptr;
|
||||
SeafDirent *dent;
|
||||
|
||||
if (!seaf_fs_manager_object_exists(seaf->fs_mgr,
|
||||
tdata->repo_id,
|
||||
tdata->repo_version,
|
||||
dir_id)) {
|
||||
tdata->fetch_objs = g_list_prepend (tdata->fetch_objs, g_strdup(dir_id));
|
||||
return;
|
||||
}
|
||||
|
||||
dir = seaf_fs_manager_get_seafdir (seaf->fs_mgr,
|
||||
tdata->repo_id,
|
||||
tdata->repo_version,
|
||||
dir_id);
|
||||
if (!dir) {
|
||||
/* corrupt dir object */
|
||||
tdata->fetch_objs = g_list_prepend (tdata->fetch_objs, g_strdup(dir_id));
|
||||
return;
|
||||
}
|
||||
|
||||
for (ptr = dir->entries; ptr; ptr = ptr->next) {
|
||||
dent = ptr->data;
|
||||
|
||||
/* Don't check objects that have been checked before. */
|
||||
if (g_hash_table_lookup (tdata->fs_objects, dent->id))
|
||||
continue;
|
||||
|
||||
g_hash_table_insert (tdata->fs_objects, g_strdup(dent->id), (gpointer)1);
|
||||
|
||||
if (!seaf_fs_manager_object_exists(seaf->fs_mgr,
|
||||
tdata->repo_id,
|
||||
tdata->repo_version,
|
||||
dent->id)) {
|
||||
tdata->fetch_objs = g_list_prepend (tdata->fetch_objs, g_strdup(dent->id));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (S_ISDIR(dent->mode)) {
|
||||
check_seafdir (tdata, dent->id);
|
||||
} else if (S_ISREG (dent->mode) && tdata->is_clone) {
|
||||
/* Only check seafile object integrity when clone.
|
||||
* This is for the purpose of recovery.
|
||||
* In ordinary sync, checking every file object's integrity would
|
||||
* take too much CPU time.
|
||||
*/
|
||||
gboolean ok;
|
||||
gboolean err = FALSE;
|
||||
ok = seaf_fs_manager_verify_seafile (seaf->fs_mgr,
|
||||
tdata->repo_id,
|
||||
tdata->repo_version,
|
||||
dent->id, TRUE, &err);
|
||||
if (!ok && !err) {
|
||||
seaf_warning ("File object %.8s is corrupt, recover from server.\n",
|
||||
dent->id);
|
||||
tdata->fetch_objs = g_list_prepend (tdata->fetch_objs, g_strdup(dent->id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
seaf_dir_free (dir);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
check_end_condition (SeafileGetfsProcPriv *priv)
|
||||
{
|
||||
return (g_queue_get_length (priv->inspect_queue) == 0 &&
|
||||
priv->pending_objects == 0 &&
|
||||
!priv->worker_checking);
|
||||
}
|
||||
|
||||
static int
|
||||
check_fs_tree_from (ThreadData *tdata, const char *root_id);
|
||||
|
||||
static void
|
||||
end_or_check_next_dir (CcnetProcessor *processor, SeafileGetfsProcPriv *priv)
|
||||
{
|
||||
if (check_end_condition (priv)) {
|
||||
seaf_debug ("Get fs end.\n");
|
||||
ccnet_processor_send_update (processor, SC_END, SS_END, NULL, 0);
|
||||
ccnet_processor_done (processor, TRUE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (priv->worker_checking) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Trigger checking the next dir. */
|
||||
char *next_dir_id = g_queue_pop_head (priv->inspect_queue);
|
||||
if (next_dir_id) {
|
||||
if (check_fs_tree_from (priv->tdata, next_dir_id) < 0) {
|
||||
transfer_task_set_error (((SeafileGetfsProc *)processor)->tx_task,
|
||||
TASK_ERR_DOWNLOAD_FS);
|
||||
ccnet_processor_send_update (processor, SC_SHUTDOWN, SS_SHUTDOWN, NULL, 0);
|
||||
ccnet_processor_done (processor, FALSE);
|
||||
}
|
||||
g_free (next_dir_id);
|
||||
}
|
||||
}
|
||||
|
||||
static void *
|
||||
check_objects_thread (void *vdata)
|
||||
{
|
||||
ThreadData *tdata = vdata;
|
||||
int cmd;
|
||||
|
||||
/* Hold one reference for worker thread. */
|
||||
thread_data_ref (tdata);
|
||||
|
||||
while (1) {
|
||||
int n = piperead (tdata->cmd_pipe, (char*)&cmd, sizeof(cmd));
|
||||
if (n < 0) {
|
||||
seaf_warning ("Failed to read commnd pipe: %s.\n", strerror(errno));
|
||||
goto out;
|
||||
}
|
||||
if (n == 0) {
|
||||
seaf_message ("Getfs proc is done, worker thread exits.\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
check_seafdir (tdata, tdata->root_id);
|
||||
|
||||
cevent_manager_add_event (seaf->ev_mgr, tdata->cevent_id, tdata);
|
||||
}
|
||||
|
||||
out:
|
||||
pipeclose (tdata->cmd_pipe);
|
||||
thread_data_unref (tdata);
|
||||
return vdata;
|
||||
}
|
||||
|
||||
static void
|
||||
check_objects_done (CEvent *event, void *unused)
|
||||
{
|
||||
ThreadData *tdata = event->data;
|
||||
CcnetProcessor *processor = tdata->processor;
|
||||
USE_PRIV;
|
||||
GList *ptr;
|
||||
char *obj_id;
|
||||
|
||||
priv->worker_checking = FALSE;
|
||||
|
||||
request_object_batch_begin (priv);
|
||||
for (ptr = tdata->fetch_objs; ptr; ptr = ptr->next) {
|
||||
obj_id = ptr->data;
|
||||
request_object_batch (processor, priv, obj_id);
|
||||
g_free (obj_id);
|
||||
}
|
||||
request_object_batch_flush (processor, priv);
|
||||
g_list_free (tdata->fetch_objs);
|
||||
tdata->fetch_objs = NULL;
|
||||
|
||||
end_or_check_next_dir (processor, priv);
|
||||
}
|
||||
|
||||
static int
|
||||
check_fs_tree_from (ThreadData *tdata, const char *root_id)
|
||||
{
|
||||
CcnetProcessor *processor = tdata->processor;
|
||||
USE_PRIV;
|
||||
|
||||
memcpy (tdata->root_id, root_id, 40);
|
||||
tdata->fetch_objs = NULL;
|
||||
|
||||
int cmd = 1;
|
||||
pipewrite (priv->cmd_pipe[1], (char*)&cmd, sizeof(cmd));
|
||||
|
||||
priv->worker_checking = TRUE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
fs_object_write_cb (OSAsyncResult *res, void *data)
|
||||
{
|
||||
CcnetProcessor *processor = data;
|
||||
TransferTask *task = ((SeafileGetfsProc *)processor)->tx_task;
|
||||
USE_PRIV;
|
||||
|
||||
if (!res->success) {
|
||||
seaf_warning ("Failed to write object %.8s.\n", res->obj_id);
|
||||
transfer_task_set_error (task, TASK_ERR_DOWNLOAD_FS);
|
||||
ccnet_processor_send_update (processor, SC_SHUTDOWN, SS_SHUTDOWN, NULL, 0);
|
||||
ccnet_processor_done (processor, FALSE);
|
||||
return;
|
||||
}
|
||||
|
||||
seaf_debug ("Written object %.8s.\n", res->obj_id);
|
||||
|
||||
--(priv->pending_objects);
|
||||
|
||||
int type = seaf_metadata_type_from_data (res->obj_id, res->data, res->len,
|
||||
(task->repo_version > 0));
|
||||
if (type == SEAF_METADATA_TYPE_DIR)
|
||||
g_queue_push_tail (priv->inspect_queue, g_strdup(res->obj_id));
|
||||
|
||||
end_or_check_next_dir (processor, priv);
|
||||
}
|
||||
|
||||
static int
|
||||
save_fs_object (SeafileGetfsProcPriv *priv, ObjectPack *pack, int len)
|
||||
{
|
||||
return seaf_obj_store_async_write (seaf->fs_mgr->obj_store,
|
||||
priv->writer_id,
|
||||
pack->id,
|
||||
pack->object,
|
||||
len - 41,
|
||||
FALSE);
|
||||
}
|
||||
|
||||
static int
|
||||
recv_fs_object (CcnetProcessor *processor, char *content, int clen)
|
||||
{
|
||||
USE_PRIV;
|
||||
ObjectPack *pack = (ObjectPack *)content;
|
||||
/* TransferTask *task = ((SeafileGetfsProc *)processor)->tx_task; */
|
||||
|
||||
if (clen < sizeof(ObjectPack)) {
|
||||
seaf_warning ("[getfs] invalid object id.\n");
|
||||
goto bad;
|
||||
}
|
||||
|
||||
/* TODO: check fs object integrity. */
|
||||
|
||||
if (save_fs_object (priv, pack, clen) < 0) {
|
||||
goto bad;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
bad:
|
||||
seaf_warning ("Bad fs object received.\n");
|
||||
transfer_task_set_error (((SeafileGetfsProc *)processor)->tx_task,
|
||||
TASK_ERR_DOWNLOAD_FS);
|
||||
ccnet_processor_send_update (processor, SC_BAD_OBJECT, SS_BAD_OBJECT,
|
||||
NULL, 0);
|
||||
ccnet_processor_done (processor, FALSE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void
|
||||
recv_fs_object_seg (CcnetProcessor *processor, char *content, int clen)
|
||||
{
|
||||
USE_PRIV;
|
||||
|
||||
/* Append the received object segment to the end */
|
||||
priv->obj_seg = g_realloc (priv->obj_seg, priv->obj_seg_len + clen);
|
||||
memcpy (priv->obj_seg + priv->obj_seg_len, content, clen);
|
||||
|
||||
seaf_debug ("Get obj seg: <id= %40s, offset= %d, lenth= %d>\n",
|
||||
priv->obj_seg, priv->obj_seg_len, clen);
|
||||
|
||||
priv->obj_seg_len += clen;
|
||||
}
|
||||
|
||||
static void
|
||||
process_fs_object_seg (CcnetProcessor *processor)
|
||||
{
|
||||
USE_PRIV;
|
||||
|
||||
if (recv_fs_object (processor, priv->obj_seg, priv->obj_seg_len) == 0) {
|
||||
g_free (priv->obj_seg);
|
||||
priv->obj_seg = NULL;
|
||||
priv->obj_seg_len = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
start_worker_thread (CcnetProcessor *processor)
|
||||
{
|
||||
SeafileGetfsProc *proc = (SeafileGetfsProc *)processor;
|
||||
USE_PRIV;
|
||||
ThreadData *tdata;
|
||||
|
||||
if (ccnet_pipe (priv->cmd_pipe) < 0)
|
||||
return -1;
|
||||
priv->cevent_id = cevent_manager_register (seaf->ev_mgr,
|
||||
check_objects_done,
|
||||
processor);
|
||||
|
||||
tdata = g_new0 (ThreadData, 1);
|
||||
tdata->cmd_pipe = priv->cmd_pipe[0];
|
||||
tdata->cevent_id = priv->cevent_id;
|
||||
tdata->processor = processor;
|
||||
tdata->is_clone = proc->tx_task->is_clone;
|
||||
tdata->fs_objects = g_hash_table_new_full (g_str_hash, g_str_equal,
|
||||
g_free, NULL);
|
||||
memcpy (tdata->repo_id, proc->tx_task->repo_id, 36);
|
||||
tdata->repo_version = proc->tx_task->repo_version;
|
||||
|
||||
/* Hold one reference for the main thread. */
|
||||
thread_data_ref (tdata);
|
||||
|
||||
priv->tdata = tdata;
|
||||
|
||||
ccnet_job_manager_schedule_job (seaf->job_mgr,
|
||||
check_objects_thread,
|
||||
NULL,
|
||||
tdata);
|
||||
priv->worker_started = TRUE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
load_fsroot_list (CcnetProcessor *processor)
|
||||
{
|
||||
USE_PRIV;
|
||||
SeafileGetfsProc *proc = (SeafileGetfsProc *) processor;
|
||||
ObjectList *ol = proc->tx_task->fs_roots;
|
||||
int i;
|
||||
int ollen = object_list_length (ol);
|
||||
|
||||
for (i = 0; i < ollen; i++) {
|
||||
g_queue_push_tail (priv->inspect_queue,
|
||||
g_strdup(g_ptr_array_index(ol->obj_ids, i)));
|
||||
}
|
||||
|
||||
/* Kick start fs object checking. */
|
||||
end_or_check_next_dir (processor, priv);
|
||||
}
|
||||
|
||||
static void
|
||||
handle_response (CcnetProcessor *processor,
|
||||
char *code, char *code_msg,
|
||||
char *content, int clen)
|
||||
{
|
||||
SeafileGetfsProc *proc = (SeafileGetfsProc *)processor;
|
||||
TransferTask *task = proc->tx_task;
|
||||
|
||||
switch (processor->state) {
|
||||
case REQUEST_SENT:
|
||||
if (strncmp(code, SC_OK, 3) == 0) {
|
||||
if (start_worker_thread (processor) < 0) {
|
||||
ccnet_processor_send_update (processor, SC_SHUTDOWN, SS_SHUTDOWN,
|
||||
NULL, 0);
|
||||
ccnet_processor_done (processor, FALSE);
|
||||
return;
|
||||
}
|
||||
load_fsroot_list (processor);
|
||||
processor->state = FETCH_OBJECT;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case FETCH_OBJECT:
|
||||
if (strncmp(code, SC_OBJ_SEG, 3) == 0) {
|
||||
recv_fs_object_seg (processor, content, clen);
|
||||
return;
|
||||
|
||||
} else if (strncmp(code, SC_OBJ_SEG_END, 3) == 0) {
|
||||
recv_fs_object_seg (processor, content, clen);
|
||||
process_fs_object_seg (processor);
|
||||
return;
|
||||
|
||||
} else if (strncmp(code, SC_OBJECT, 3) == 0) {
|
||||
recv_fs_object (processor, content, clen);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
g_return_if_reached ();
|
||||
}
|
||||
|
||||
seaf_warning ("Bad response: %s %s.\n", code, code_msg);
|
||||
if (memcmp (code, SC_ACCESS_DENIED, 3) == 0)
|
||||
transfer_task_set_error (task, TASK_ERR_ACCESS_DENIED);
|
||||
ccnet_processor_done (processor, FALSE);
|
||||
}
|
||||
|
||||
static int
|
||||
start (CcnetProcessor *processor, int argc, char **argv)
|
||||
{
|
||||
USE_PRIV;
|
||||
TransferTask *task = ((SeafileGetfsProc *)processor)->tx_task;
|
||||
GString *buf = g_string_new (NULL);
|
||||
|
||||
if (task->session_token)
|
||||
g_string_printf (buf, "remote %s seafile-putfs %s",
|
||||
processor->peer_id, task->session_token);
|
||||
else
|
||||
g_string_printf (buf, "remote %s seafile-putfs",
|
||||
processor->peer_id);
|
||||
ccnet_processor_send_request (processor, buf->str);
|
||||
g_string_free (buf, TRUE);
|
||||
|
||||
processor->state = REQUEST_SENT;
|
||||
priv->inspect_queue = g_queue_new ();
|
||||
|
||||
priv->writer_id = seaf_obj_store_register_async_write (seaf->fs_mgr->obj_store,
|
||||
task->repo_id,
|
||||
task->repo_version,
|
||||
fs_object_write_cb,
|
||||
processor);
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#ifndef SEAFILE_GETFS_PROC_H
|
||||
#define SEAFILE_GETFS_PROC_H
|
||||
|
||||
#include <glib-object.h>
|
||||
#include "transfer-mgr.h"
|
||||
|
||||
#define SEAFILE_TYPE_GETFS_PROC (seafile_getfs_proc_get_type ())
|
||||
#define SEAFILE_GETFS_PROC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SEAFILE_TYPE_GETFS_PROC, SeafileGetfsProc))
|
||||
#define SEAFILE_IS_GETFS_PROC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SEAFILE_TYPE_GETFS_PROC))
|
||||
#define SEAFILE_GETFS_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SEAFILE_TYPE_GETFS_PROC, SeafileGetfsProcClass))
|
||||
#define IS_SEAFILE_GETFS_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SEAFILE_TYPE_GETFS_PROC))
|
||||
#define SEAFILE_GETFS_PROC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SEAFILE_TYPE_GETFS_PROC, SeafileGetfsProcClass))
|
||||
|
||||
typedef struct _SeafileGetfsProc SeafileGetfsProc;
|
||||
typedef struct _SeafileGetfsProcClass SeafileGetfsProcClass;
|
||||
|
||||
struct _SeafileGetfsProc {
|
||||
CcnetProcessor parent_instance;
|
||||
|
||||
TransferTask *tx_task;
|
||||
};
|
||||
|
||||
struct _SeafileGetfsProcClass {
|
||||
CcnetProcessorClass parent_class;
|
||||
};
|
||||
|
||||
GType seafile_getfs_proc_get_type ();
|
||||
|
||||
#endif
|
||||
|
@ -1,443 +0,0 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#define DEBUG_FLAG SEAFILE_DEBUG_TRANSFER
|
||||
#include "log.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <ccnet.h>
|
||||
#include "utils.h"
|
||||
|
||||
#include "seafile-session.h"
|
||||
#include "fs-mgr.h"
|
||||
#include "processors/objecttx-common.h"
|
||||
#include "getfs-v2-proc.h"
|
||||
#include "seaf-utils.h"
|
||||
|
||||
/*
|
||||
* putfs-v2-proc server-head-id [client-head-id]
|
||||
* ------------------------------>
|
||||
* OK
|
||||
* <-----------------------------
|
||||
*
|
||||
* The server uses diff to calculate objects to put
|
||||
*
|
||||
* SC_OBJ_LIST_SEG
|
||||
* <-----------------------------
|
||||
* ......
|
||||
* SC_OBJ_LIST_SEG_END
|
||||
* <-----------------------------
|
||||
*
|
||||
* The client calculates the list of objects to get
|
||||
*
|
||||
* SC_OBJ_LIST_SEG
|
||||
* ----------------------------->
|
||||
* ......
|
||||
* SC_OBJ
|
||||
* <----------------------------
|
||||
* ......
|
||||
* SC_END
|
||||
* ----------------------------->
|
||||
*
|
||||
* After all objects are written to disk, the client ends the protocol
|
||||
*/
|
||||
|
||||
enum {
|
||||
INIT = 0,
|
||||
CHECK_OBJECT_LIST,
|
||||
GET_OBJECTS,
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
char *obj_seg;
|
||||
int obj_seg_len;
|
||||
|
||||
gboolean registered;
|
||||
guint32 writer_id;
|
||||
|
||||
/* Used to check object list */
|
||||
GList *recv_objs;
|
||||
GList *needed_objs;
|
||||
|
||||
int n_pending;
|
||||
int n_saved;
|
||||
} SeafileGetfsProcPriv;
|
||||
|
||||
#define GET_PRIV(o) \
|
||||
(G_TYPE_INSTANCE_GET_PRIVATE ((o), SEAFILE_TYPE_GETFS_V2_PROC, SeafileGetfsProcPriv))
|
||||
|
||||
#define USE_PRIV \
|
||||
SeafileGetfsProcPriv *priv = GET_PRIV(processor);
|
||||
|
||||
|
||||
G_DEFINE_TYPE (SeafileGetfsV2Proc, seafile_getfs_v2_proc, CCNET_TYPE_PROCESSOR)
|
||||
|
||||
static int start (CcnetProcessor *processor, int argc, char **argv);
|
||||
static void handle_response (CcnetProcessor *processor,
|
||||
char *code, char *code_msg,
|
||||
char *content, int clen);
|
||||
|
||||
static void
|
||||
release_resource(CcnetProcessor *processor)
|
||||
{
|
||||
USE_PRIV;
|
||||
|
||||
g_free (priv->obj_seg);
|
||||
|
||||
if (priv->registered) {
|
||||
seaf_obj_store_unregister_async_write (seaf->fs_mgr->obj_store,
|
||||
priv->writer_id);
|
||||
}
|
||||
|
||||
string_list_free (priv->recv_objs);
|
||||
string_list_free (priv->needed_objs);
|
||||
|
||||
CCNET_PROCESSOR_CLASS (seafile_getfs_v2_proc_parent_class)->release_resource (processor);
|
||||
}
|
||||
|
||||
static void
|
||||
seafile_getfs_v2_proc_class_init (SeafileGetfsV2ProcClass *klass)
|
||||
{
|
||||
CcnetProcessorClass *proc_class = CCNET_PROCESSOR_CLASS (klass);
|
||||
|
||||
proc_class->name = "getfs-v2-proc";
|
||||
proc_class->start = start;
|
||||
proc_class->handle_response = handle_response;
|
||||
proc_class->release_resource = release_resource;
|
||||
|
||||
g_type_class_add_private (klass, sizeof (SeafileGetfsProcPriv));
|
||||
}
|
||||
|
||||
static void
|
||||
seafile_getfs_v2_proc_init (SeafileGetfsV2Proc *processor)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
on_fs_write (OSAsyncResult *res, void *cb_data);
|
||||
|
||||
static int
|
||||
start (CcnetProcessor *processor, int argc, char **argv)
|
||||
{
|
||||
USE_PRIV;
|
||||
GString *buf;
|
||||
SeafileGetfsV2Proc *proc = (SeafileGetfsV2Proc *)processor;
|
||||
TransferTask *task = proc->tx_task;
|
||||
|
||||
buf = g_string_new (NULL);
|
||||
if (!task->is_clone) {
|
||||
SeafBranch *master = seaf_branch_manager_get_branch (seaf->branch_mgr,
|
||||
task->repo_id,
|
||||
"master");
|
||||
if (!master) {
|
||||
seaf_warning ("Master branch not found for repo %s.\n", task->repo_id);
|
||||
g_string_free (buf, TRUE);
|
||||
ccnet_processor_done (processor, FALSE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
g_string_printf (buf, "remote %s seafile-putfs-v2 %s %s %s",
|
||||
processor->peer_id, task->session_token,
|
||||
task->head, master->commit_id);
|
||||
|
||||
seaf_branch_unref (master);
|
||||
} else
|
||||
g_string_printf (buf, "remote %s seafile-putfs-v2 %s %s",
|
||||
processor->peer_id, task->session_token,
|
||||
task->head);
|
||||
|
||||
ccnet_processor_send_request (processor, buf->str);
|
||||
g_string_free (buf, TRUE);
|
||||
|
||||
priv->registered = TRUE;
|
||||
priv->writer_id = seaf_obj_store_register_async_write (seaf->fs_mgr->obj_store,
|
||||
task->repo_id,
|
||||
task->repo_version,
|
||||
on_fs_write,
|
||||
processor);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
send_object_list_segment (CcnetProcessor *processor);
|
||||
|
||||
static void
|
||||
on_fs_write (OSAsyncResult *res, void *cb_data)
|
||||
{
|
||||
CcnetProcessor *processor = cb_data;
|
||||
USE_PRIV;
|
||||
|
||||
if (!res->success) {
|
||||
seaf_warning ("[getfs] Failed to write %s.\n", res->obj_id);
|
||||
ccnet_processor_send_update (processor, SC_BAD_OBJECT, SS_BAD_OBJECT,
|
||||
NULL, 0);
|
||||
ccnet_processor_done (processor, FALSE);
|
||||
return;
|
||||
}
|
||||
|
||||
seaf_debug ("[getfs] Wrote fs object %s.\n", res->obj_id);
|
||||
|
||||
if (++(priv->n_saved) == priv->n_pending)
|
||||
send_object_list_segment (processor);
|
||||
}
|
||||
|
||||
static int
|
||||
save_fs_object (CcnetProcessor *processor, ObjectPack *pack, int len)
|
||||
{
|
||||
USE_PRIV;
|
||||
|
||||
return seaf_obj_store_async_write (seaf->fs_mgr->obj_store,
|
||||
priv->writer_id,
|
||||
pack->id,
|
||||
pack->object,
|
||||
len - 41,
|
||||
FALSE);
|
||||
}
|
||||
|
||||
static int
|
||||
recv_fs_object (CcnetProcessor *processor, char *content, int clen)
|
||||
{
|
||||
ObjectPack *pack = (ObjectPack *)content;
|
||||
/* SeafFSObject *fs_obj = NULL; */
|
||||
|
||||
if (clen < sizeof(ObjectPack)) {
|
||||
seaf_warning ("invalid object id.\n");
|
||||
goto bad;
|
||||
}
|
||||
|
||||
seaf_debug ("[getfs] Recv fs object %.8s.\n", pack->id);
|
||||
|
||||
/* Check object integrity by parsing it. */
|
||||
/* fs_obj = seaf_fs_object_from_data(pack->id, */
|
||||
/* pack->object, clen - sizeof(ObjectPack), */
|
||||
/* (priv->repo_version > 0)); */
|
||||
/* if (!fs_obj) { */
|
||||
/* seaf_warning ("Bad fs object %s.\n", pack->id); */
|
||||
/* goto bad; */
|
||||
/* } */
|
||||
|
||||
/* seaf_fs_object_free (fs_obj); */
|
||||
|
||||
if (save_fs_object (processor, pack, clen) < 0) {
|
||||
goto bad;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
bad:
|
||||
ccnet_processor_send_update (processor, SC_BAD_OBJECT,
|
||||
SS_BAD_OBJECT, NULL, 0);
|
||||
seaf_warning ("[getfs] Bad fs object received.\n");
|
||||
ccnet_processor_done (processor, FALSE);
|
||||
|
||||
/* seaf_fs_object_free (fs_obj); */
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void
|
||||
recv_fs_object_seg (CcnetProcessor *processor, char *content, int clen)
|
||||
{
|
||||
USE_PRIV;
|
||||
|
||||
/* Append the received object segment to the end */
|
||||
priv->obj_seg = g_realloc (priv->obj_seg, priv->obj_seg_len + clen);
|
||||
memcpy (priv->obj_seg + priv->obj_seg_len, content, clen);
|
||||
|
||||
seaf_debug ("[getfs] Get obj seg: <id= %40s, offset= %d, lenth= %d>\n",
|
||||
priv->obj_seg, priv->obj_seg_len, clen);
|
||||
|
||||
priv->obj_seg_len += clen;
|
||||
}
|
||||
|
||||
static void
|
||||
process_fs_object_seg (CcnetProcessor *processor)
|
||||
{
|
||||
USE_PRIV;
|
||||
|
||||
if (recv_fs_object (processor, priv->obj_seg, priv->obj_seg_len) == 0) {
|
||||
g_free (priv->obj_seg);
|
||||
priv->obj_seg = NULL;
|
||||
priv->obj_seg_len = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void *
|
||||
calculate_needed_object_list (void *data)
|
||||
{
|
||||
CcnetProcessor *processor = data;
|
||||
USE_PRIV;
|
||||
SeafileGetfsV2Proc *proc = (SeafileGetfsV2Proc *)processor;
|
||||
TransferTask *task = proc->tx_task;
|
||||
GList *ptr;
|
||||
char *obj_id;
|
||||
GHashTable *checked_objs;
|
||||
int dummy;
|
||||
|
||||
checked_objs = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
|
||||
|
||||
for (ptr = priv->recv_objs; ptr; ptr = ptr->next) {
|
||||
obj_id = ptr->data;
|
||||
|
||||
if (g_hash_table_lookup (checked_objs, obj_id)) {
|
||||
g_free (obj_id);
|
||||
continue;
|
||||
}
|
||||
g_hash_table_insert (checked_objs, g_strdup(obj_id), &dummy);
|
||||
|
||||
if (!seaf_obj_store_obj_exists (seaf->fs_mgr->obj_store,
|
||||
task->repo_id, task->repo_version,
|
||||
obj_id))
|
||||
priv->needed_objs = g_list_prepend (priv->needed_objs, obj_id);
|
||||
else
|
||||
g_free (obj_id);
|
||||
}
|
||||
|
||||
g_hash_table_destroy (checked_objs);
|
||||
g_list_free (priv->recv_objs);
|
||||
priv->recv_objs = NULL;
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
#define OBJECT_LIST_SEGMENT_N 1000
|
||||
#define OBJECT_LIST_SEGMENT_LEN 40 * 1000
|
||||
|
||||
static void
|
||||
send_object_list_segment (CcnetProcessor *processor)
|
||||
{
|
||||
USE_PRIV;
|
||||
char buf[OBJECT_LIST_SEGMENT_LEN];
|
||||
|
||||
if (priv->needed_objs == NULL) {
|
||||
seaf_debug ("All objects saved. Done.\n");
|
||||
ccnet_processor_send_update (processor, SC_END, SS_END, NULL, 0);
|
||||
ccnet_processor_done (processor, TRUE);
|
||||
return;
|
||||
}
|
||||
|
||||
priv->n_pending = 0;
|
||||
priv->n_saved = 0;
|
||||
|
||||
int i = 0;
|
||||
char *p = buf;
|
||||
char *obj_id;
|
||||
while (priv->needed_objs != NULL) {
|
||||
obj_id = priv->needed_objs->data;
|
||||
priv->needed_objs = g_list_delete_link (priv->needed_objs,
|
||||
priv->needed_objs);
|
||||
|
||||
memcpy (p, obj_id, 40);
|
||||
p += 40;
|
||||
g_free (obj_id);
|
||||
if (++i == OBJECT_LIST_SEGMENT_N)
|
||||
break;
|
||||
}
|
||||
|
||||
if (i > 0) {
|
||||
seaf_debug ("Send %d object ids.\n", i);
|
||||
priv->n_pending = i;
|
||||
ccnet_processor_send_update (processor,
|
||||
SC_OBJ_LIST_SEG, SS_OBJ_LIST_SEG,
|
||||
buf, i * 40);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
calculate_needed_object_list_done (void *data)
|
||||
{
|
||||
CcnetProcessor *processor = data;
|
||||
send_object_list_segment (processor);
|
||||
processor->state = GET_OBJECTS;
|
||||
}
|
||||
|
||||
static void
|
||||
process_recv_object_list (CcnetProcessor *processor, char *content, int clen)
|
||||
{
|
||||
USE_PRIV;
|
||||
int n, i;
|
||||
char *p;
|
||||
char *obj_id;
|
||||
|
||||
n = clen/40;
|
||||
p = content;
|
||||
|
||||
seaf_debug ("Recv %d object ids.\n", n);
|
||||
|
||||
for (i = 0; i < n; ++i) {
|
||||
obj_id = g_strndup (p, 40);
|
||||
priv->recv_objs = g_list_prepend (priv->recv_objs, obj_id);
|
||||
p += 40;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
handle_response (CcnetProcessor *processor,
|
||||
char *code, char *code_msg,
|
||||
char *content, int clen)
|
||||
{
|
||||
switch (processor->state) {
|
||||
case INIT:
|
||||
if (strncmp (code, SC_OK, 3) == 0)
|
||||
processor->state = CHECK_OBJECT_LIST;
|
||||
else {
|
||||
seaf_warning ("Bad response: %s %s\n", code, code_msg);
|
||||
ccnet_processor_done (processor, FALSE);
|
||||
}
|
||||
break;
|
||||
case CHECK_OBJECT_LIST:
|
||||
if (strncmp (code, SC_OBJ_LIST_SEG, 3) == 0) {
|
||||
if (clen % 40 != 0) {
|
||||
seaf_warning ("Invalid object list segment length %d.\n", clen);
|
||||
ccnet_processor_send_update (processor,
|
||||
SC_SHUTDOWN, SS_SHUTDOWN,
|
||||
NULL, 0);
|
||||
ccnet_processor_done (processor, FALSE);
|
||||
return;
|
||||
}
|
||||
|
||||
process_recv_object_list (processor, content, clen);
|
||||
|
||||
} else if (strncmp (code, SC_OBJ_LIST_SEG_END, 3) == 0) {
|
||||
|
||||
ccnet_processor_thread_create (processor, seaf->job_mgr,
|
||||
calculate_needed_object_list,
|
||||
calculate_needed_object_list_done,
|
||||
processor);
|
||||
|
||||
} else if (strncmp (code, SC_END, 3) == 0) {
|
||||
/* The server finds nothing to put. */
|
||||
ccnet_processor_done (processor, TRUE);
|
||||
} else {
|
||||
seaf_warning ("Bad response: %s %s\n", code, code_msg);
|
||||
ccnet_processor_send_update (processor,
|
||||
SC_BAD_RESPONSE_CODE, SS_BAD_RESPONSE_CODE,
|
||||
NULL, 0);
|
||||
ccnet_processor_done (processor, FALSE);
|
||||
}
|
||||
break;
|
||||
case GET_OBJECTS:
|
||||
if (strncmp(code, SC_OBJ_SEG, 3) == 0) {
|
||||
recv_fs_object_seg (processor, content, clen);
|
||||
} else if (strncmp(code, SC_OBJ_SEG_END, 3) == 0) {
|
||||
recv_fs_object_seg (processor, content, clen);
|
||||
process_fs_object_seg (processor);
|
||||
} else if (strncmp(code, SC_OBJECT, 3) == 0) {
|
||||
recv_fs_object (processor, content, clen);
|
||||
} else {
|
||||
seaf_warning ("Bad response: %s %s\n", code, code_msg);
|
||||
ccnet_processor_send_update (processor,
|
||||
SC_BAD_RESPONSE_CODE, SS_BAD_RESPONSE_CODE,
|
||||
NULL, 0);
|
||||
ccnet_processor_done (processor, FALSE);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
g_return_if_reached ();
|
||||
}
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#ifndef SEAFILE_GETFS_V2_PROC_H
|
||||
#define SEAFILE_GETFS_V2_PROC_H
|
||||
|
||||
#include <glib-object.h>
|
||||
#include <ccnet/processor.h>
|
||||
#include "transfer-mgr.h"
|
||||
|
||||
#define SEAFILE_TYPE_GETFS_V2_PROC (seafile_getfs_v2_proc_get_type ())
|
||||
#define SEAFILE_GETFS_V2_PROC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SEAFILE_TYPE_GETFS_V2_PROC, SeafileGetfsV2Proc))
|
||||
#define SEAFILE_IS_GETFS_V2_PROC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SEAFILE_TYPE_GETFS_V2_PROC))
|
||||
#define SEAFILE_GETFS_V2_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SEAFILE_TYPE_GETFS_V2_PROC, SeafileGetfsV2ProcClass))
|
||||
#define IS_SEAFILE_GETFS_V2_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SEAFILE_TYPE_GETFS_V2_PROC))
|
||||
#define SEAFILE_GETFS_V2_PROC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SEAFILE_TYPE_GETFS_V2_PROC, SeafileGetfsV2ProcClass))
|
||||
|
||||
typedef struct _SeafileGetfsV2Proc SeafileGetfsV2Proc;
|
||||
typedef struct _SeafileGetfsV2ProcClass SeafileGetfsV2ProcClass;
|
||||
|
||||
struct _SeafileGetfsV2Proc {
|
||||
CcnetProcessor parent_instance;
|
||||
|
||||
TransferTask *tx_task;
|
||||
};
|
||||
|
||||
struct _SeafileGetfsV2ProcClass {
|
||||
CcnetProcessorClass parent_class;
|
||||
};
|
||||
|
||||
GType seafile_getfs_v2_proc_get_type ();
|
||||
|
||||
#endif
|
||||
|
@ -1,105 +0,0 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <ccnet.h>
|
||||
#include "sendbranch-proc.h"
|
||||
|
||||
#include "log.h"
|
||||
|
||||
#define SC_NOT_FF "402"
|
||||
#define SS_NOT_FF "Not fast forward"
|
||||
#define SC_QUOTA_ERROR "403"
|
||||
#define SS_QUOTA_ERROR "Failed to get quota"
|
||||
#define SC_QUOTA_FULL "404"
|
||||
#define SS_QUOTA_FULL "storage for the repo's owner is full"
|
||||
#define SC_ACCESS_DENIED "410"
|
||||
#define SS_ACCESS_DENIED "Access denied"
|
||||
|
||||
G_DEFINE_TYPE (SeafileSendbranchProc, seafile_sendbranch_proc, CCNET_TYPE_PROCESSOR)
|
||||
|
||||
static int start (CcnetProcessor *processor, int argc, char **argv);
|
||||
static void handle_response (CcnetProcessor *processor,
|
||||
char *code, char *code_msg,
|
||||
char *content, int clen);
|
||||
|
||||
static void
|
||||
release_resource(CcnetProcessor *processor)
|
||||
{
|
||||
/* FILL IT */
|
||||
|
||||
CCNET_PROCESSOR_CLASS (seafile_sendbranch_proc_parent_class)->release_resource (processor);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
seafile_sendbranch_proc_class_init (SeafileSendbranchProcClass *klass)
|
||||
{
|
||||
CcnetProcessorClass *proc_class = CCNET_PROCESSOR_CLASS (klass);
|
||||
|
||||
proc_class->start = start;
|
||||
proc_class->handle_response = handle_response;
|
||||
proc_class->release_resource = release_resource;
|
||||
proc_class->name = "sendbranch-proc";
|
||||
}
|
||||
|
||||
static void
|
||||
seafile_sendbranch_proc_init (SeafileSendbranchProc *processor)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
start (CcnetProcessor *processor, int argc, char **argv)
|
||||
{
|
||||
char *repo_id, *branch, *new_head;
|
||||
GString *buf;
|
||||
TransferTask *task = ((SeafileSendbranchProc *)processor)->task;
|
||||
|
||||
if (argc != 3) {
|
||||
return -1;
|
||||
}
|
||||
repo_id = argv[0];
|
||||
branch = argv[1];
|
||||
new_head = argv[2];
|
||||
|
||||
buf = g_string_new (NULL);
|
||||
if (task->protocol_version <= 6)
|
||||
g_string_printf (buf, "remote %s seafile-recvbranch %s %s %s %s",
|
||||
processor->peer_id, repo_id, branch, new_head,
|
||||
task->session_token);
|
||||
else
|
||||
g_string_printf (buf, "remote %s seafile-recvbranch-v2 %s %s %s %s",
|
||||
processor->peer_id, repo_id, branch, new_head,
|
||||
task->session_token);
|
||||
ccnet_processor_send_request (processor, buf->str);
|
||||
g_string_free (buf, TRUE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
handle_response (CcnetProcessor *processor,
|
||||
char *code, char *code_msg,
|
||||
char *content, int clen)
|
||||
{
|
||||
SeafileSendbranchProc *proc = (SeafileSendbranchProc *)processor;
|
||||
TransferTask *task = proc->task;
|
||||
|
||||
if (memcmp (code, SC_OK, 3) == 0) {
|
||||
ccnet_processor_done (processor, TRUE);
|
||||
} else {
|
||||
seaf_warning ("[sendbranch] Bad response: %s.\n", code_msg);
|
||||
if (strncmp(code, SC_NOT_FF, 3) == 0)
|
||||
transfer_task_set_error (task, TASK_ERR_NOT_FAST_FORWARD);
|
||||
else if (strncmp(code, SC_QUOTA_ERROR, 3) == 0)
|
||||
transfer_task_set_error (task, TASK_ERR_CHECK_QUOTA);
|
||||
else if (strncmp(code, SC_QUOTA_FULL, 3) == 0)
|
||||
transfer_task_set_error (task, TASK_ERR_QUOTA_FULL);
|
||||
else if (strncmp(code, SC_ACCESS_DENIED, 3) == 0)
|
||||
transfer_task_set_error (task, TASK_ERR_ACCESS_DENIED);
|
||||
else
|
||||
transfer_task_set_error (task, TASK_ERR_UNKNOWN);
|
||||
ccnet_processor_done (processor, FALSE);
|
||||
}
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#ifndef SEAFILE_SENDBRANCH_PROC_H
|
||||
#define SEAFILE_SENDBRANCH_PROC_H
|
||||
|
||||
#include <glib-object.h>
|
||||
#include <ccnet/processor.h>
|
||||
|
||||
#include "transfer-mgr.h"
|
||||
|
||||
#define SEAFILE_TYPE_SENDBRANCH_PROC (seafile_sendbranch_proc_get_type ())
|
||||
#define SEAFILE_SENDBRANCH_PROC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SEAFILE_TYPE_SENDBRANCH_PROC, SeafileSendbranchProc))
|
||||
#define SEAFILE_IS_SENDBRANCH_PROC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SEAFILE_TYPE_SENDBRANCH_PROC))
|
||||
#define SEAFILE_SENDBRANCH_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SEAFILE_TYPE_SENDBRANCH_PROC, SeafileSendbranchProcClass))
|
||||
#define IS_SEAFILE_SENDBRANCH_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SEAFILE_TYPE_SENDBRANCH_PROC))
|
||||
#define SEAFILE_SENDBRANCH_PROC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SEAFILE_TYPE_SENDBRANCH_PROC, SeafileSendbranchProcClass))
|
||||
|
||||
typedef struct _SeafileSendbranchProc SeafileSendbranchProc;
|
||||
typedef struct _SeafileSendbranchProcClass SeafileSendbranchProcClass;
|
||||
|
||||
struct _SeafileSendbranchProc {
|
||||
CcnetProcessor parent_instance;
|
||||
|
||||
TransferTask *task;
|
||||
};
|
||||
|
||||
struct _SeafileSendbranchProcClass {
|
||||
CcnetProcessorClass parent_class;
|
||||
};
|
||||
|
||||
GType seafile_sendbranch_proc_get_type ();
|
||||
|
||||
#endif
|
||||
|
@ -1,331 +0,0 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#define DEBUG_FLAG SEAFILE_DEBUG_TRANSFER
|
||||
#include "log.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <ccnet.h>
|
||||
#include "net.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include "seafile-session.h"
|
||||
#include "sendcommit-v3-new-proc.h"
|
||||
#include "processors/objecttx-common.h"
|
||||
#include "vc-common.h"
|
||||
|
||||
/*
|
||||
seafile-recvcommit-v3
|
||||
INIT --------------------->
|
||||
200 OK
|
||||
INIT <---------------------
|
||||
|
||||
Object
|
||||
SEND_OBJ ----------------------->
|
||||
Ack or Bad Object
|
||||
<---------------------
|
||||
|
||||
...
|
||||
|
||||
End
|
||||
----------------------->
|
||||
*/
|
||||
|
||||
enum {
|
||||
INIT,
|
||||
SEND_OBJECT
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
char remote_id[41];
|
||||
char last_uploaded_id[41];
|
||||
GList *id_list;
|
||||
gboolean visited_last_uploaded;
|
||||
gboolean compute_success;
|
||||
} SeafileSendcommitProcPriv;
|
||||
|
||||
#define GET_PRIV(o) \
|
||||
(G_TYPE_INSTANCE_GET_PRIVATE ((o), SEAFILE_TYPE_SENDCOMMIT_V3_NEW_PROC, SeafileSendcommitProcPriv))
|
||||
|
||||
#define USE_PRIV \
|
||||
SeafileSendcommitProcPriv *priv = GET_PRIV(processor);
|
||||
|
||||
static int send_commit_start (CcnetProcessor *processor, int argc, char **argv);
|
||||
static void handle_response (CcnetProcessor *processor,
|
||||
char *code, char *code_msg,
|
||||
char *content, int clen);
|
||||
|
||||
|
||||
G_DEFINE_TYPE (SeafileSendcommitV3NewProc, seafile_sendcommit_v3_new_proc, CCNET_TYPE_PROCESSOR)
|
||||
|
||||
static void
|
||||
release_resource (CcnetProcessor *processor)
|
||||
{
|
||||
USE_PRIV;
|
||||
|
||||
if (priv->id_list != NULL)
|
||||
string_list_free (priv->id_list);
|
||||
|
||||
CCNET_PROCESSOR_CLASS (seafile_sendcommit_v3_new_proc_parent_class)->release_resource (processor);
|
||||
}
|
||||
|
||||
static void
|
||||
seafile_sendcommit_v3_new_proc_class_init (SeafileSendcommitV3NewProcClass *klass)
|
||||
{
|
||||
CcnetProcessorClass *proc_class = CCNET_PROCESSOR_CLASS (klass);
|
||||
|
||||
proc_class->name = "sendcommit-v3-new-proc";
|
||||
proc_class->start = send_commit_start;
|
||||
proc_class->handle_response = handle_response;
|
||||
proc_class->release_resource = release_resource;
|
||||
|
||||
g_type_class_add_private (klass, sizeof (SeafileSendcommitProcPriv));
|
||||
}
|
||||
|
||||
static void
|
||||
seafile_sendcommit_v3_new_proc_init (SeafileSendcommitV3NewProc *processor)
|
||||
{
|
||||
}
|
||||
|
||||
static int
|
||||
send_commit_start (CcnetProcessor *processor, int argc, char **argv)
|
||||
{
|
||||
USE_PRIV;
|
||||
GString *buf;
|
||||
TransferTask *task = ((SeafileSendcommitV3NewProc *)processor)->tx_task;
|
||||
|
||||
memcpy (priv->remote_id, task->remote_head, 41);
|
||||
|
||||
/* fs_roots can be non-NULL if transfer is resumed from NET_DOWN. */
|
||||
if (task->fs_roots != NULL)
|
||||
object_list_free (task->fs_roots);
|
||||
task->fs_roots = object_list_new ();
|
||||
|
||||
if (task->commits != NULL)
|
||||
object_list_free (task->commits);
|
||||
task->commits = object_list_new ();
|
||||
|
||||
buf = g_string_new (NULL);
|
||||
g_string_printf (buf, "remote %s seafile-recvcommit-v3 %s %s",
|
||||
processor->peer_id, task->to_branch, task->session_token);
|
||||
ccnet_processor_send_request (processor, buf->str);
|
||||
g_string_free (buf, TRUE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
send_commit (CcnetProcessor *processor, const char *object_id)
|
||||
{
|
||||
TransferTask *task = ((SeafileSendcommitV3NewProc *)processor)->tx_task;
|
||||
char *data;
|
||||
int len;
|
||||
ObjectPack *pack = NULL;
|
||||
int pack_size;
|
||||
|
||||
if (seaf_obj_store_read_obj (seaf->commit_mgr->obj_store,
|
||||
task->repo_id, task->repo_version,
|
||||
object_id, (void**)&data, &len) < 0) {
|
||||
seaf_warning ("Failed to read commit %s.\n", object_id);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
pack_size = sizeof(ObjectPack) + len;
|
||||
pack = malloc (pack_size);
|
||||
memcpy (pack->id, object_id, 41);
|
||||
memcpy (pack->object, data, len);
|
||||
|
||||
ccnet_processor_send_update (processor, SC_OBJECT, SS_OBJECT,
|
||||
(char *)pack, pack_size);
|
||||
|
||||
seaf_debug ("Send commit %.8s.\n", object_id);
|
||||
|
||||
g_free (data);
|
||||
free (pack);
|
||||
return;
|
||||
|
||||
fail:
|
||||
ccnet_processor_send_update (processor, SC_NOT_FOUND, SS_NOT_FOUND,
|
||||
object_id, 41);
|
||||
ccnet_processor_done (processor, FALSE);
|
||||
}
|
||||
|
||||
static void
|
||||
send_one_commit (CcnetProcessor *processor)
|
||||
{
|
||||
USE_PRIV;
|
||||
char *commit_id;
|
||||
|
||||
if (!priv->id_list) {
|
||||
ccnet_processor_send_update (processor, SC_END, SS_END, NULL, 0);
|
||||
ccnet_processor_done (processor, TRUE);
|
||||
return;
|
||||
}
|
||||
|
||||
commit_id = priv->id_list->data;
|
||||
priv->id_list = g_list_delete_link (priv->id_list, priv->id_list);
|
||||
|
||||
send_commit (processor, commit_id);
|
||||
|
||||
g_free (commit_id);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
collect_upload_commit_ids (SeafCommit *commit, void *data, gboolean *stop)
|
||||
{
|
||||
CcnetProcessor *processor = data;
|
||||
TransferTask *task = ((SeafileSendcommitV3NewProc *)processor)->tx_task;
|
||||
USE_PRIV;
|
||||
|
||||
if (strcmp (priv->last_uploaded_id, commit->commit_id) == 0) {
|
||||
priv->visited_last_uploaded = TRUE;
|
||||
*stop = TRUE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (priv->remote_id[0] != 0 &&
|
||||
strcmp (priv->remote_id, commit->commit_id) == 0) {
|
||||
*stop = TRUE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (commit->parent_id &&
|
||||
!seaf_commit_manager_commit_exists (seaf->commit_mgr,
|
||||
commit->repo_id, commit->version,
|
||||
commit->parent_id)) {
|
||||
*stop = TRUE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (commit->second_parent_id &&
|
||||
!seaf_commit_manager_commit_exists (seaf->commit_mgr,
|
||||
commit->repo_id, commit->version,
|
||||
commit->second_parent_id)) {
|
||||
*stop = TRUE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
priv->id_list = g_list_prepend (priv->id_list, g_strdup(commit->commit_id));
|
||||
|
||||
/* We don't need to send the contents under an empty dir.
|
||||
*/
|
||||
if (strcmp (commit->root_id, EMPTY_SHA1) != 0)
|
||||
object_list_insert (task->fs_roots, commit->root_id);
|
||||
|
||||
object_list_insert (task->commits, commit->commit_id);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void *
|
||||
compute_upload_commits_thread (void *vdata)
|
||||
{
|
||||
CcnetProcessor *processor = vdata;
|
||||
SeafileSendcommitV3NewProc *proc = (SeafileSendcommitV3NewProc *)processor;
|
||||
TransferTask *task = proc->tx_task;
|
||||
USE_PRIV;
|
||||
gboolean ret;
|
||||
|
||||
ret = seaf_commit_manager_traverse_commit_tree_truncated (seaf->commit_mgr,
|
||||
task->repo_id,
|
||||
task->repo_version,
|
||||
task->head,
|
||||
collect_upload_commit_ids,
|
||||
processor, FALSE);
|
||||
if (!ret) {
|
||||
priv->compute_success = FALSE;
|
||||
return vdata;
|
||||
}
|
||||
|
||||
/* We have to make sure all commits that need to be uploaded are found locally.
|
||||
* If we have traversed up to the last uploaded commit, we've traversed all
|
||||
* needed commits.
|
||||
*/
|
||||
if (!priv->visited_last_uploaded) {
|
||||
seaf_warning ("Not all commit objects need to be uploaded exist locally.\n");
|
||||
priv->compute_success = FALSE;
|
||||
return vdata;
|
||||
}
|
||||
|
||||
priv->compute_success = TRUE;
|
||||
return vdata;
|
||||
}
|
||||
|
||||
static void
|
||||
compute_upload_commits_done (void *vdata)
|
||||
{
|
||||
CcnetProcessor *processor = vdata;
|
||||
USE_PRIV;
|
||||
|
||||
if (!priv->compute_success) {
|
||||
ccnet_processor_send_update (processor, SC_NOT_FOUND, SS_NOT_FOUND,
|
||||
NULL, 0);
|
||||
ccnet_processor_done (processor, FALSE);
|
||||
return;
|
||||
}
|
||||
|
||||
send_one_commit (processor);
|
||||
}
|
||||
|
||||
static void
|
||||
send_commits (CcnetProcessor *processor, const char *head)
|
||||
{
|
||||
SeafileSendcommitV3NewProc *proc = (SeafileSendcommitV3NewProc *)processor;
|
||||
USE_PRIV;
|
||||
char *last_uploaded;
|
||||
|
||||
last_uploaded = seaf_repo_manager_get_repo_property (seaf->repo_mgr,
|
||||
proc->tx_task->repo_id,
|
||||
REPO_LOCAL_HEAD);
|
||||
if (!last_uploaded || strlen(last_uploaded) != 40) {
|
||||
seaf_warning ("Last uploaded commit id is not found in db or invalid.\n");
|
||||
ccnet_processor_send_update (processor, SC_SHUTDOWN, SS_SHUTDOWN, NULL, 0);
|
||||
ccnet_processor_done (processor, FALSE);
|
||||
return;
|
||||
}
|
||||
memcpy (priv->last_uploaded_id, last_uploaded, 40);
|
||||
g_free (last_uploaded);
|
||||
|
||||
ccnet_processor_thread_create (processor,
|
||||
seaf->job_mgr,
|
||||
compute_upload_commits_thread,
|
||||
compute_upload_commits_done,
|
||||
processor);
|
||||
}
|
||||
|
||||
static void handle_response (CcnetProcessor *processor,
|
||||
char *code, char *code_msg,
|
||||
char *content, int clen)
|
||||
{
|
||||
SeafileSendcommitV3NewProc *proc = (SeafileSendcommitV3NewProc *)processor;
|
||||
TransferTask *task = proc->tx_task;
|
||||
if (task->state != TASK_STATE_NORMAL) {
|
||||
ccnet_processor_done (processor, TRUE);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (processor->state) {
|
||||
case INIT:
|
||||
if (memcmp (code, SC_OK, 3) == 0) {
|
||||
processor->state = SEND_OBJECT;
|
||||
send_commits (processor, task->head);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case SEND_OBJECT:
|
||||
if (memcmp (code, SC_ACK, 3) == 0) {
|
||||
send_one_commit (processor);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
g_return_if_reached ();
|
||||
}
|
||||
|
||||
seaf_warning ("Bad response: %s %s.\n", code, code_msg);
|
||||
if (memcmp (code, SC_ACCESS_DENIED, 3) == 0)
|
||||
transfer_task_set_error (task, TASK_ERR_ACCESS_DENIED);
|
||||
ccnet_processor_done (processor, FALSE);
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#ifndef SEAFILE_SENDCOMMIT_V3_NEW_PROC_H
|
||||
#define SEAFILE_SENDCOMMIT_V3_NEW_PROC_H
|
||||
|
||||
#include <glib-object.h>
|
||||
|
||||
|
||||
#define SEAFILE_TYPE_SENDCOMMIT_V3_NEW_PROC (seafile_sendcommit_v3_new_proc_get_type ())
|
||||
#define SEAFILE_SENDCOMMIT_V3_NEW_PROC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SEAFILE_TYPE_SENDCOMMIT_V3_NEW_PROC, SeafileSendcommitV3NewProc))
|
||||
#define SEAFILE_IS_SENDCOMMIT_V3_NEW_PROC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SEAFILE_TYPE_SENDCOMMIT_V3_NEW_PROC))
|
||||
#define SEAFILE_SENDCOMMIT_V3_NEW_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SEAFILE_TYPE_SENDCOMMIT_V3_NEW_PROC, SeafileSendcommitV3NewProcClass))
|
||||
#define IS_SEAFILE_SENDCOMMIT_V3_NEW_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SEAFILE_TYPE_SENDCOMMIT_V3_NEW_PROC))
|
||||
#define SEAFILE_SENDCOMMIT_V3_NEW_PROC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SEAFILE_TYPE_SENDCOMMIT_V3_NEW_PROC, SeafileSendcommitV3NewProcClass))
|
||||
|
||||
typedef struct _SeafileSendcommitV3NewProc SeafileSendcommitV3NewProc;
|
||||
typedef struct _SeafileSendcommitV3NewProcClass SeafileSendcommitV3NewProcClass;
|
||||
|
||||
struct _SeafileSendcommitV3NewProc {
|
||||
CcnetProcessor parent_instance;
|
||||
|
||||
TransferTask *tx_task;
|
||||
};
|
||||
|
||||
struct _SeafileSendcommitV3NewProcClass {
|
||||
CcnetProcessorClass parent_class;
|
||||
};
|
||||
|
||||
GType seafile_sendcommit_v3_new_proc_get_type ();
|
||||
|
||||
#endif
|
@ -1,396 +0,0 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#define DEBUG_FLAG SEAFILE_DEBUG_TRANSFER
|
||||
#include "log.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <ccnet.h>
|
||||
#include "net.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include "seafile-session.h"
|
||||
#include "sendcommit-v3-proc.h"
|
||||
#include "processors/objecttx-common.h"
|
||||
#include "vc-common.h"
|
||||
|
||||
/*
|
||||
seafile-recvcommit-v3
|
||||
INIT --------------------->
|
||||
200 OK
|
||||
INIT <---------------------
|
||||
|
||||
Object
|
||||
SEND_OBJ ----------------------->
|
||||
Ack or Bad Object
|
||||
<---------------------
|
||||
|
||||
...
|
||||
|
||||
End
|
||||
----------------------->
|
||||
*/
|
||||
|
||||
enum {
|
||||
INIT,
|
||||
SEND_OBJECT
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
char remote_id[41];
|
||||
GList *id_list;
|
||||
GHashTable *commit_hash;
|
||||
gboolean fast_forward;
|
||||
gboolean compute_success;
|
||||
} SeafileSendcommitProcPriv;
|
||||
|
||||
#define GET_PRIV(o) \
|
||||
(G_TYPE_INSTANCE_GET_PRIVATE ((o), SEAFILE_TYPE_SENDCOMMIT_V3_PROC, SeafileSendcommitProcPriv))
|
||||
|
||||
#define USE_PRIV \
|
||||
SeafileSendcommitProcPriv *priv = GET_PRIV(processor);
|
||||
|
||||
static int send_commit_start (CcnetProcessor *processor, int argc, char **argv);
|
||||
static void handle_response (CcnetProcessor *processor,
|
||||
char *code, char *code_msg,
|
||||
char *content, int clen);
|
||||
|
||||
|
||||
G_DEFINE_TYPE (SeafileSendcommitV3Proc, seafile_sendcommit_v3_proc, CCNET_TYPE_PROCESSOR)
|
||||
|
||||
static void
|
||||
release_resource (CcnetProcessor *processor)
|
||||
{
|
||||
USE_PRIV;
|
||||
|
||||
if (priv->id_list != NULL)
|
||||
string_list_free (priv->id_list);
|
||||
if (priv->commit_hash)
|
||||
g_hash_table_destroy (priv->commit_hash);
|
||||
|
||||
CCNET_PROCESSOR_CLASS (seafile_sendcommit_v3_proc_parent_class)->release_resource (processor);
|
||||
}
|
||||
|
||||
static void
|
||||
seafile_sendcommit_v3_proc_class_init (SeafileSendcommitV3ProcClass *klass)
|
||||
{
|
||||
CcnetProcessorClass *proc_class = CCNET_PROCESSOR_CLASS (klass);
|
||||
|
||||
proc_class->name = "sendcommit-v3-proc";
|
||||
proc_class->start = send_commit_start;
|
||||
proc_class->handle_response = handle_response;
|
||||
proc_class->release_resource = release_resource;
|
||||
|
||||
g_type_class_add_private (klass, sizeof (SeafileSendcommitProcPriv));
|
||||
}
|
||||
|
||||
static void
|
||||
seafile_sendcommit_v3_proc_init (SeafileSendcommitV3Proc *processor)
|
||||
{
|
||||
}
|
||||
|
||||
static int
|
||||
send_commit_start (CcnetProcessor *processor, int argc, char **argv)
|
||||
{
|
||||
USE_PRIV;
|
||||
GString *buf;
|
||||
TransferTask *task = ((SeafileSendcommitV3Proc *)processor)->tx_task;
|
||||
|
||||
memcpy (priv->remote_id, task->remote_head, 41);
|
||||
|
||||
/* fs_roots can be non-NULL if transfer is resumed from NET_DOWN. */
|
||||
if (task->fs_roots != NULL)
|
||||
object_list_free (task->fs_roots);
|
||||
task->fs_roots = object_list_new ();
|
||||
|
||||
if (task->commits != NULL)
|
||||
object_list_free (task->commits);
|
||||
task->commits = object_list_new ();
|
||||
|
||||
buf = g_string_new (NULL);
|
||||
g_string_printf (buf, "remote %s seafile-recvcommit-v3 %s %s",
|
||||
processor->peer_id, task->to_branch, task->session_token);
|
||||
ccnet_processor_send_request (processor, buf->str);
|
||||
g_string_free (buf, TRUE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
send_commit (CcnetProcessor *processor, const char *object_id)
|
||||
{
|
||||
TransferTask *task = ((SeafileSendcommitV3Proc *)processor)->tx_task;
|
||||
char *data;
|
||||
int len;
|
||||
ObjectPack *pack = NULL;
|
||||
int pack_size;
|
||||
|
||||
if (seaf_obj_store_read_obj (seaf->commit_mgr->obj_store,
|
||||
task->repo_id, task->repo_version,
|
||||
object_id, (void**)&data, &len) < 0) {
|
||||
seaf_warning ("Failed to read commit %s.\n", object_id);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
pack_size = sizeof(ObjectPack) + len;
|
||||
pack = malloc (pack_size);
|
||||
memcpy (pack->id, object_id, 41);
|
||||
memcpy (pack->object, data, len);
|
||||
|
||||
ccnet_processor_send_update (processor, SC_OBJECT, SS_OBJECT,
|
||||
(char *)pack, pack_size);
|
||||
|
||||
seaf_debug ("Send commit %.8s.\n", object_id);
|
||||
|
||||
g_free (data);
|
||||
free (pack);
|
||||
return;
|
||||
|
||||
fail:
|
||||
ccnet_processor_send_update (processor, SC_NOT_FOUND, SS_NOT_FOUND,
|
||||
object_id, 41);
|
||||
ccnet_processor_done (processor, FALSE);
|
||||
}
|
||||
|
||||
static void
|
||||
send_one_commit (CcnetProcessor *processor)
|
||||
{
|
||||
USE_PRIV;
|
||||
char *commit_id;
|
||||
|
||||
if (!priv->id_list) {
|
||||
ccnet_processor_send_update (processor, SC_END, SS_END, NULL, 0);
|
||||
ccnet_processor_done (processor, TRUE);
|
||||
return;
|
||||
}
|
||||
|
||||
commit_id = priv->id_list->data;
|
||||
priv->id_list = g_list_delete_link (priv->id_list, priv->id_list);
|
||||
|
||||
send_commit (processor, commit_id);
|
||||
|
||||
g_free (commit_id);
|
||||
}
|
||||
|
||||
/* Traverse the commit graph until remote_id is met or a merged commit
|
||||
* (commit with two parents) is met.
|
||||
*
|
||||
* If a merged commit is met before remote_id, that implies that
|
||||
* we did a real merge when merged with the branch headed by remote_id.
|
||||
* In this case we'll need more computation to find out the "delta" commits
|
||||
* between these two branches. Otherwise, if the merge was a fast-forward
|
||||
* one, it's enough to just send all the commits between our head commit
|
||||
* and remote_id.
|
||||
*/
|
||||
static gboolean
|
||||
traverse_commit_fast_forward (SeafCommit *commit, void *data, gboolean *stop)
|
||||
{
|
||||
CcnetProcessor *processor = data;
|
||||
TransferTask *task = ((SeafileSendcommitV3Proc *)processor)->tx_task;
|
||||
USE_PRIV;
|
||||
|
||||
if (priv->remote_id[0] != 0 &&
|
||||
strcmp (priv->remote_id, commit->commit_id) == 0) {
|
||||
*stop = TRUE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (commit->second_parent_id != NULL) {
|
||||
*stop = TRUE;
|
||||
priv->fast_forward = FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
priv->id_list = g_list_prepend (priv->id_list, g_strdup(commit->commit_id));
|
||||
|
||||
/* We don't need to send the contents under an empty dir.
|
||||
*/
|
||||
if (strcmp (commit->root_id, EMPTY_SHA1) != 0)
|
||||
object_list_insert (task->fs_roots, commit->root_id);
|
||||
|
||||
object_list_insert (task->commits, commit->commit_id);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
traverse_commit_remote (SeafCommit *commit, void *data, gboolean *stop)
|
||||
{
|
||||
CcnetProcessor *processor = data;
|
||||
USE_PRIV;
|
||||
char *key;
|
||||
|
||||
if (g_hash_table_lookup (priv->commit_hash, commit->commit_id))
|
||||
return TRUE;
|
||||
|
||||
key = g_strdup(commit->commit_id);
|
||||
g_hash_table_replace (priv->commit_hash, key, key);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
compute_delta (SeafCommit *commit, void *data, gboolean *stop)
|
||||
{
|
||||
CcnetProcessor *processor = data;
|
||||
TransferTask *task = ((SeafileSendcommitV3Proc *)processor)->tx_task;
|
||||
USE_PRIV;
|
||||
|
||||
if (!g_hash_table_lookup (priv->commit_hash, commit->commit_id)) {
|
||||
priv->id_list = g_list_prepend (priv->id_list,
|
||||
g_strdup(commit->commit_id));
|
||||
|
||||
if (strcmp (commit->root_id, EMPTY_SHA1) != 0)
|
||||
object_list_insert (task->fs_roots, commit->root_id);
|
||||
|
||||
object_list_insert (task->commits, commit->commit_id);
|
||||
} else {
|
||||
/* Stop traversing down from this commit if it already exists
|
||||
* in the remote branch.
|
||||
*/
|
||||
*stop = TRUE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static int
|
||||
compute_delta_commits (CcnetProcessor *processor, const char *head)
|
||||
{
|
||||
gboolean ret;
|
||||
TransferTask *task = ((SeafileSendcommitV3Proc *)processor)->tx_task;
|
||||
USE_PRIV;
|
||||
|
||||
string_list_free (priv->id_list);
|
||||
priv->id_list = NULL;
|
||||
|
||||
object_list_free (task->fs_roots);
|
||||
task->fs_roots = object_list_new ();
|
||||
|
||||
object_list_free (task->commits);
|
||||
task->commits = object_list_new ();
|
||||
|
||||
priv->commit_hash = g_hash_table_new_full (g_str_hash, g_str_equal,
|
||||
g_free, NULL);
|
||||
|
||||
ret = seaf_commit_manager_traverse_commit_tree (seaf->commit_mgr,
|
||||
task->repo_id,
|
||||
task->repo_version,
|
||||
priv->remote_id,
|
||||
traverse_commit_remote,
|
||||
processor, FALSE);
|
||||
if (!ret) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = seaf_commit_manager_traverse_commit_tree (seaf->commit_mgr,
|
||||
task->repo_id,
|
||||
task->repo_version,
|
||||
head,
|
||||
compute_delta,
|
||||
processor, FALSE);
|
||||
if (!ret) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *
|
||||
compute_upload_commits_thread (void *vdata)
|
||||
{
|
||||
CcnetProcessor *processor = vdata;
|
||||
SeafileSendcommitV3Proc *proc = (SeafileSendcommitV3Proc *)processor;
|
||||
TransferTask *task = proc->tx_task;
|
||||
USE_PRIV;
|
||||
gboolean ret;
|
||||
|
||||
priv->fast_forward = TRUE;
|
||||
ret = seaf_commit_manager_traverse_commit_tree (seaf->commit_mgr,
|
||||
task->repo_id,
|
||||
task->repo_version,
|
||||
task->head,
|
||||
traverse_commit_fast_forward,
|
||||
processor, FALSE);
|
||||
if (!ret) {
|
||||
priv->compute_success = FALSE;
|
||||
return vdata;
|
||||
}
|
||||
|
||||
if (priv->fast_forward) {
|
||||
priv->compute_success = TRUE;
|
||||
seaf_debug ("[sendcommt] Send commit after a fast forward merge.\n");
|
||||
return vdata;
|
||||
}
|
||||
|
||||
seaf_debug ("[sendcommit] Send commit after a real merge.\n");
|
||||
if (compute_delta_commits (processor, task->head) < 0) {
|
||||
priv->compute_success = FALSE;
|
||||
return vdata;
|
||||
}
|
||||
|
||||
priv->compute_success = TRUE;
|
||||
return vdata;
|
||||
}
|
||||
|
||||
static void
|
||||
compute_upload_commits_done (void *vdata)
|
||||
{
|
||||
CcnetProcessor *processor = vdata;
|
||||
USE_PRIV;
|
||||
|
||||
if (!priv->compute_success) {
|
||||
ccnet_processor_send_update (processor, SC_NOT_FOUND, SS_NOT_FOUND,
|
||||
NULL, 0);
|
||||
ccnet_processor_done (processor, FALSE);
|
||||
return;
|
||||
}
|
||||
|
||||
send_one_commit (processor);
|
||||
}
|
||||
|
||||
static void
|
||||
send_commits (CcnetProcessor *processor, const char *head)
|
||||
{
|
||||
ccnet_processor_thread_create (processor,
|
||||
seaf->job_mgr,
|
||||
compute_upload_commits_thread,
|
||||
compute_upload_commits_done,
|
||||
processor);
|
||||
}
|
||||
|
||||
static void handle_response (CcnetProcessor *processor,
|
||||
char *code, char *code_msg,
|
||||
char *content, int clen)
|
||||
{
|
||||
SeafileSendcommitV3Proc *proc = (SeafileSendcommitV3Proc *)processor;
|
||||
TransferTask *task = proc->tx_task;
|
||||
if (task->state != TASK_STATE_NORMAL) {
|
||||
ccnet_processor_done (processor, TRUE);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (processor->state) {
|
||||
case INIT:
|
||||
if (memcmp (code, SC_OK, 3) == 0) {
|
||||
processor->state = SEND_OBJECT;
|
||||
send_commits (processor, task->head);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case SEND_OBJECT:
|
||||
if (memcmp (code, SC_ACK, 3) == 0) {
|
||||
send_one_commit (processor);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
g_return_if_reached ();
|
||||
}
|
||||
|
||||
seaf_warning ("Bad response: %s %s.\n", code, code_msg);
|
||||
if (memcmp (code, SC_ACCESS_DENIED, 3) == 0)
|
||||
transfer_task_set_error (task, TASK_ERR_ACCESS_DENIED);
|
||||
ccnet_processor_done (processor, FALSE);
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#ifndef SEAFILE_SENDCOMMIT_V3_PROC_H
|
||||
#define SEAFILE_SENDCOMMIT_V3_PROC_H
|
||||
|
||||
#include <glib-object.h>
|
||||
|
||||
|
||||
#define SEAFILE_TYPE_SENDCOMMIT_V3_PROC (seafile_sendcommit_v3_proc_get_type ())
|
||||
#define SEAFILE_SENDCOMMIT_V3_PROC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SEAFILE_TYPE_SENDCOMMIT_V3_PROC, SeafileSendcommitV3Proc))
|
||||
#define SEAFILE_IS_SENDCOMMIT_V3_PROC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SEAFILE_TYPE_SENDCOMMIT_V3_PROC))
|
||||
#define SEAFILE_SENDCOMMIT_V3_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SEAFILE_TYPE_SENDCOMMIT_V3_PROC, SeafileSendcommitV3ProcClass))
|
||||
#define IS_SEAFILE_SENDCOMMIT_V3_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SEAFILE_TYPE_SENDCOMMIT_V3_PROC))
|
||||
#define SEAFILE_SENDCOMMIT_V3_PROC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SEAFILE_TYPE_SENDCOMMIT_V3_PROC, SeafileSendcommitV3ProcClass))
|
||||
|
||||
typedef struct _SeafileSendcommitV3Proc SeafileSendcommitV3Proc;
|
||||
typedef struct _SeafileSendcommitV3ProcClass SeafileSendcommitV3ProcClass;
|
||||
|
||||
struct _SeafileSendcommitV3Proc {
|
||||
CcnetProcessor parent_instance;
|
||||
|
||||
TransferTask *tx_task;
|
||||
};
|
||||
|
||||
struct _SeafileSendcommitV3ProcClass {
|
||||
CcnetProcessorClass parent_class;
|
||||
};
|
||||
|
||||
GType seafile_sendcommit_v3_proc_get_type ();
|
||||
|
||||
#endif
|
@ -1,131 +0,0 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#define DEBUG_FLAG SEAFILE_DEBUG_TRANSFER
|
||||
#include "log.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <ccnet.h>
|
||||
#include "net.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include "seafile-session.h"
|
||||
#include "sendcommit-v4-proc.h"
|
||||
#include "processors/objecttx-common.h"
|
||||
#include "vc-common.h"
|
||||
|
||||
enum {
|
||||
INIT,
|
||||
SEND_OBJECT
|
||||
};
|
||||
|
||||
static int send_commit_start (CcnetProcessor *processor, int argc, char **argv);
|
||||
static void handle_response (CcnetProcessor *processor,
|
||||
char *code, char *code_msg,
|
||||
char *content, int clen);
|
||||
|
||||
|
||||
G_DEFINE_TYPE (SeafileSendcommitV4Proc, seafile_sendcommit_v4_proc, CCNET_TYPE_PROCESSOR)
|
||||
|
||||
static void
|
||||
seafile_sendcommit_v4_proc_class_init (SeafileSendcommitV4ProcClass *klass)
|
||||
{
|
||||
CcnetProcessorClass *proc_class = CCNET_PROCESSOR_CLASS (klass);
|
||||
|
||||
proc_class->name = "sendcommit-v4-proc";
|
||||
proc_class->start = send_commit_start;
|
||||
proc_class->handle_response = handle_response;
|
||||
}
|
||||
|
||||
static void
|
||||
seafile_sendcommit_v4_proc_init (SeafileSendcommitV4Proc *processor)
|
||||
{
|
||||
}
|
||||
|
||||
static int
|
||||
send_commit_start (CcnetProcessor *processor, int argc, char **argv)
|
||||
{
|
||||
TransferTask *task = ((SeafileSendcommitV4Proc *)processor)->tx_task;
|
||||
GString *buf;
|
||||
|
||||
buf = g_string_new (NULL);
|
||||
g_string_printf (buf, "remote %s seafile-recvcommit-v3 master %s",
|
||||
processor->peer_id, task->session_token);
|
||||
ccnet_processor_send_request (processor, buf->str);
|
||||
g_string_free (buf, TRUE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
send_commit (CcnetProcessor *processor, const char *object_id)
|
||||
{
|
||||
TransferTask *task = ((SeafileSendcommitV4Proc *)processor)->tx_task;
|
||||
char *data;
|
||||
int len;
|
||||
ObjectPack *pack = NULL;
|
||||
int pack_size;
|
||||
|
||||
if (seaf_obj_store_read_obj (seaf->commit_mgr->obj_store,
|
||||
task->repo_id, task->repo_version,
|
||||
object_id, (void**)&data, &len) < 0) {
|
||||
seaf_warning ("Failed to read commit %s.\n", object_id);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
pack_size = sizeof(ObjectPack) + len;
|
||||
pack = malloc (pack_size);
|
||||
memcpy (pack->id, object_id, 41);
|
||||
memcpy (pack->object, data, len);
|
||||
|
||||
ccnet_processor_send_update (processor, SC_OBJECT, SS_OBJECT,
|
||||
(char *)pack, pack_size);
|
||||
|
||||
seaf_debug ("Send commit %.8s.\n", object_id);
|
||||
|
||||
g_free (data);
|
||||
free (pack);
|
||||
return;
|
||||
|
||||
fail:
|
||||
ccnet_processor_send_update (processor, SC_NOT_FOUND, SS_NOT_FOUND,
|
||||
object_id, 41);
|
||||
ccnet_processor_done (processor, FALSE);
|
||||
}
|
||||
|
||||
static void handle_response (CcnetProcessor *processor,
|
||||
char *code, char *code_msg,
|
||||
char *content, int clen)
|
||||
{
|
||||
SeafileSendcommitV4Proc *proc = (SeafileSendcommitV4Proc *)processor;
|
||||
TransferTask *task = proc->tx_task;
|
||||
if (task->state != TASK_STATE_NORMAL) {
|
||||
ccnet_processor_done (processor, TRUE);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (processor->state) {
|
||||
case INIT:
|
||||
if (memcmp (code, SC_OK, 3) == 0) {
|
||||
processor->state = SEND_OBJECT;
|
||||
send_commit (processor, task->head);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case SEND_OBJECT:
|
||||
if (memcmp (code, SC_ACK, 3) == 0) {
|
||||
ccnet_processor_done (processor, TRUE);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
g_return_if_reached ();
|
||||
}
|
||||
|
||||
seaf_warning ("Bad response: %s %s.\n", code, code_msg);
|
||||
if (memcmp (code, SC_ACCESS_DENIED, 3) == 0)
|
||||
transfer_task_set_error (task, TASK_ERR_ACCESS_DENIED);
|
||||
ccnet_processor_done (processor, FALSE);
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#ifndef SEAFILE_SENDCOMMIT_V4_PROC_H
|
||||
#define SEAFILE_SENDCOMMIT_V4_PROC_H
|
||||
|
||||
#include <glib-object.h>
|
||||
|
||||
|
||||
#define SEAFILE_TYPE_SENDCOMMIT_V4_PROC (seafile_sendcommit_v4_proc_get_type ())
|
||||
#define SEAFILE_SENDCOMMIT_V4_PROC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SEAFILE_TYPE_SENDCOMMIT_V4_PROC, SeafileSendcommitV4Proc))
|
||||
#define SEAFILE_IS_SENDCOMMIT_V4_PROC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SEAFILE_TYPE_SENDCOMMIT_V4_PROC))
|
||||
#define SEAFILE_SENDCOMMIT_V4_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SEAFILE_TYPE_SENDCOMMIT_V4_PROC, SeafileSendcommitV4ProcClass))
|
||||
#define IS_SEAFILE_SENDCOMMIT_V4_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SEAFILE_TYPE_SENDCOMMIT_V4_PROC))
|
||||
#define SEAFILE_SENDCOMMIT_V4_PROC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SEAFILE_TYPE_SENDCOMMIT_V4_PROC, SeafileSendcommitV4ProcClass))
|
||||
|
||||
typedef struct _SeafileSendcommitV4Proc SeafileSendcommitV4Proc;
|
||||
typedef struct _SeafileSendcommitV4ProcClass SeafileSendcommitV4ProcClass;
|
||||
|
||||
struct _SeafileSendcommitV4Proc {
|
||||
CcnetProcessor parent_instance;
|
||||
|
||||
TransferTask *tx_task;
|
||||
};
|
||||
|
||||
struct _SeafileSendcommitV4ProcClass {
|
||||
CcnetProcessorClass parent_class;
|
||||
};
|
||||
|
||||
GType seafile_sendcommit_v4_proc_get_type ();
|
||||
|
||||
#endif
|
@ -1,288 +0,0 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
|
||||
/*
|
||||
* file system synchronization algorithm:
|
||||
*
|
||||
* Begins with the root directory object,
|
||||
*
|
||||
* seafile-recvfs
|
||||
* S(INIT -> SEND_ROOT) ---------------------------------> T
|
||||
* OK
|
||||
* S(SEND_ROOT) <--------------------------- T
|
||||
* FS_ROOT
|
||||
* S(SEND_ROOT) ----------------------------> T
|
||||
* OK
|
||||
* S(SEND_ROOT) <---------------------------- T
|
||||
* FS Root, FS Root End
|
||||
* S(SEND_ROOT -> SEND_OBJECT) ----------------------------> T
|
||||
*
|
||||
* Get Object
|
||||
* S(SEND_OBJECT) <---------------------------- T
|
||||
* Object
|
||||
* S(SEND_OBJECT) ----------------------------> T
|
||||
* .
|
||||
* .
|
||||
* .
|
||||
* END
|
||||
* S(SEND_OBJECT) <--------------------------- T
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#define DEBUG_FLAG SEAFILE_DEBUG_TRANSFER
|
||||
#include "log.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <ccnet.h>
|
||||
#include "utils.h"
|
||||
|
||||
#include "seafile-session.h"
|
||||
#include "commit-mgr.h"
|
||||
#include "fs-mgr.h"
|
||||
#include "processors/objecttx-common.h"
|
||||
#include "sendfs-proc.h"
|
||||
|
||||
enum {
|
||||
INIT,
|
||||
SEND_ROOT,
|
||||
SEND_OBJECT
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
guint32 reader_id;
|
||||
} SeafileSendfsProcPriv;
|
||||
|
||||
#define GET_PRIV(o) \
|
||||
(G_TYPE_INSTANCE_GET_PRIVATE ((o), SEAFILE_TYPE_SENDFS_PROC, SeafileSendfsProcPriv))
|
||||
|
||||
#define USE_PRIV \
|
||||
SeafileSendfsProcPriv *priv = GET_PRIV(processor);
|
||||
|
||||
G_DEFINE_TYPE (SeafileSendfsProc, seafile_sendfs_proc, CCNET_TYPE_PROCESSOR)
|
||||
|
||||
static int start (CcnetProcessor *processor, int argc, char **argv);
|
||||
static void handle_response (CcnetProcessor *processor,
|
||||
char *code, char *code_msg,
|
||||
char *content, int clen);
|
||||
|
||||
static void
|
||||
release_resource(CcnetProcessor *processor)
|
||||
{
|
||||
USE_PRIV;
|
||||
|
||||
seaf_obj_store_unregister_async_read (seaf->fs_mgr->obj_store,
|
||||
priv->reader_id);
|
||||
|
||||
CCNET_PROCESSOR_CLASS (seafile_sendfs_proc_parent_class)->release_resource (processor);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
seafile_sendfs_proc_class_init (SeafileSendfsProcClass *klass)
|
||||
{
|
||||
CcnetProcessorClass *proc_class = CCNET_PROCESSOR_CLASS (klass);
|
||||
|
||||
proc_class->name = "sendfs-proc";
|
||||
proc_class->start = start;
|
||||
proc_class->handle_response = handle_response;
|
||||
proc_class->release_resource = release_resource;
|
||||
|
||||
g_type_class_add_private (klass, sizeof(SeafileSendfsProcPriv));
|
||||
}
|
||||
|
||||
static void
|
||||
seafile_sendfs_proc_init (SeafileSendfsProc *processor)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
fs_object_read_cb (OSAsyncResult *res, void *data);
|
||||
|
||||
static int
|
||||
start (CcnetProcessor *processor, int argc, char **argv)
|
||||
{
|
||||
USE_PRIV;
|
||||
GString *buf;
|
||||
SeafileSendfsProc *proc = (SeafileSendfsProc *)processor;
|
||||
TransferTask *task = proc->tx_task;
|
||||
|
||||
buf = g_string_new (NULL);
|
||||
g_string_printf (buf, "remote %s seafile-recvfs %s",
|
||||
processor->peer_id, task->session_token);
|
||||
ccnet_processor_send_request (processor, buf->str);
|
||||
g_string_free (buf, TRUE);
|
||||
|
||||
processor->state = SEND_ROOT;
|
||||
proc->last_idx = 0;
|
||||
|
||||
priv->reader_id = seaf_obj_store_register_async_read (seaf->fs_mgr->obj_store,
|
||||
task->repo_id,
|
||||
task->repo_version,
|
||||
fs_object_read_cb,
|
||||
processor);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
send_fs_object (CcnetProcessor *processor,
|
||||
const char *object_id, char *data, int len)
|
||||
{
|
||||
ObjectPack *pack = NULL;
|
||||
int pack_size;
|
||||
|
||||
pack_size = sizeof(ObjectPack) + len;
|
||||
pack = malloc (pack_size);
|
||||
memcpy (pack->id, object_id, 41);
|
||||
memcpy (pack->object, data, len);
|
||||
|
||||
if (pack_size <= MAX_OBJ_SEG_SIZE) {
|
||||
ccnet_processor_send_update (processor, SC_OBJECT, SS_OBJECT,
|
||||
(char *)pack, pack_size);
|
||||
} else {
|
||||
int offset, n;
|
||||
|
||||
offset = 0;
|
||||
while (offset < pack_size) {
|
||||
n = MIN(pack_size - offset, MAX_OBJ_SEG_SIZE);
|
||||
|
||||
if (offset + n < pack_size) {
|
||||
ccnet_processor_send_update (processor,
|
||||
SC_OBJ_SEG, SS_OBJ_SEG,
|
||||
(char *)pack + offset, n);
|
||||
} else {
|
||||
ccnet_processor_send_update (processor,
|
||||
SC_OBJ_SEG_END, SS_OBJ_SEG_END,
|
||||
(char *)pack + offset, n);
|
||||
}
|
||||
|
||||
seaf_debug ("Sent object %s segment<total = %d, offset = %d, n = %d>\n",
|
||||
object_id, pack_size, offset, n);
|
||||
|
||||
offset += n;
|
||||
}
|
||||
}
|
||||
|
||||
seaf_debug ("Send fs object %.8s.\n", object_id);
|
||||
|
||||
free (pack);
|
||||
}
|
||||
|
||||
static void
|
||||
fs_object_read_cb (OSAsyncResult *res, void *data)
|
||||
{
|
||||
CcnetProcessor *processor = data;
|
||||
|
||||
if (!res->success) {
|
||||
seaf_warning ("Failed to read fs object %.8s.\n", res->obj_id);
|
||||
ccnet_processor_send_update (processor, SC_NOT_FOUND, SS_NOT_FOUND,
|
||||
res->obj_id, 41);
|
||||
ccnet_processor_done (processor, FALSE);
|
||||
return;
|
||||
}
|
||||
|
||||
send_fs_object (processor, res->obj_id, res->data, res->len);
|
||||
}
|
||||
|
||||
static void
|
||||
read_fs_object (CcnetProcessor *processor, const char *obj_id)
|
||||
{
|
||||
USE_PRIV;
|
||||
|
||||
seaf_obj_store_async_read (seaf->fs_mgr->obj_store,
|
||||
priv->reader_id,
|
||||
obj_id);
|
||||
}
|
||||
|
||||
static void
|
||||
send_fs_objects (CcnetProcessor *processor, char *content, int clen)
|
||||
{
|
||||
char *object_id;
|
||||
int n_objects;
|
||||
int i;
|
||||
|
||||
if (clen % 41 != 1 || content[clen-1] != '\0') {
|
||||
seaf_warning ("Bad fs object list.\n");
|
||||
ccnet_processor_send_update (processor, SC_BAD_OL, SS_BAD_OL, NULL, 0);
|
||||
ccnet_processor_done (processor, FALSE);
|
||||
return;
|
||||
}
|
||||
|
||||
n_objects = clen/41;
|
||||
|
||||
object_id = content;
|
||||
for (i = 0; i < n_objects; ++i) {
|
||||
object_id[40] = '\0';
|
||||
read_fs_object (processor, object_id);
|
||||
object_id += 41;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
send_fs_roots (CcnetProcessor *processor)
|
||||
{
|
||||
SeafileSendfsProc *proc = (SeafileSendfsProc *)processor;
|
||||
char buf[2096];
|
||||
char *ptr = buf;
|
||||
int i, count = 0;
|
||||
ObjectList *ol = proc->tx_task->fs_roots;
|
||||
int ollen = object_list_length (ol);
|
||||
|
||||
if (proc->last_idx == ollen) {
|
||||
ccnet_processor_send_update (processor, SC_ROOT_END, SS_ROOT_END,
|
||||
NULL, 0);
|
||||
processor->state = SEND_OBJECT;
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = proc->last_idx; i < ollen; i++) {
|
||||
memcpy (ptr, g_ptr_array_index(ol->obj_ids, i), 40);
|
||||
ptr += 40;
|
||||
*ptr++ = '\n';
|
||||
|
||||
if (++count == 48)
|
||||
break;
|
||||
}
|
||||
|
||||
ccnet_processor_send_update (processor, SC_ROOT, SS_ROOT,
|
||||
buf, 41 * count);
|
||||
proc->last_idx = i;
|
||||
}
|
||||
|
||||
static void
|
||||
handle_response (CcnetProcessor *processor,
|
||||
char *code, char *code_msg,
|
||||
char *content, int clen)
|
||||
{
|
||||
SeafileSendfsProc *proc = (SeafileSendfsProc *)processor;
|
||||
TransferTask *task = proc->tx_task;
|
||||
|
||||
switch (processor->state) {
|
||||
case SEND_ROOT:
|
||||
if (strncmp(code, SC_OK, 3) == 0) {
|
||||
send_fs_roots (processor);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case SEND_OBJECT:
|
||||
if (strncmp(code, SC_GET_OBJECT, 3) == 0) {
|
||||
send_fs_objects (processor, content, clen);
|
||||
return;
|
||||
} else if (strncmp(code, SC_END, 3) == 0) {
|
||||
seaf_debug ("Send fs objects end.\n");
|
||||
ccnet_processor_done (processor, TRUE);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
g_return_if_reached ();
|
||||
}
|
||||
|
||||
seaf_warning ("Bad response: %s %s.\n", code, code_msg);
|
||||
if (memcmp (code, SC_ACCESS_DENIED, 3) == 0)
|
||||
transfer_task_set_error (task, TASK_ERR_ACCESS_DENIED);
|
||||
ccnet_processor_done (processor, FALSE);
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#ifndef SEAFILE_SENDFS_PROC_H
|
||||
#define SEAFILE_SENDFS_PROC_H
|
||||
|
||||
#include <glib-object.h>
|
||||
#include <ccnet/processor.h>
|
||||
#include "transfer-mgr.h"
|
||||
|
||||
#define SEAFILE_TYPE_SENDFS_PROC (seafile_sendfs_proc_get_type ())
|
||||
#define SEAFILE_SENDFS_PROC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SEAFILE_TYPE_SENDFS_PROC, SeafileSendfsProc))
|
||||
#define SEAFILE_IS_SENDFS_PROC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SEAFILE_TYPE_SENDFS_PROC))
|
||||
#define SEAFILE_SENDFS_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SEAFILE_TYPE_SENDFS_PROC, SeafileSendfsProcClass))
|
||||
#define IS_SEAFILE_SENDFS_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SEAFILE_TYPE_SENDFS_PROC))
|
||||
#define SEAFILE_SENDFS_PROC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SEAFILE_TYPE_SENDFS_PROC, SeafileSendfsProcClass))
|
||||
|
||||
typedef struct _SeafileSendfsProc SeafileSendfsProc;
|
||||
typedef struct _SeafileSendfsProcClass SeafileSendfsProcClass;
|
||||
|
||||
struct _SeafileSendfsProc {
|
||||
CcnetProcessor parent_instance;
|
||||
|
||||
TransferTask *tx_task;
|
||||
int last_idx; /* used in send root fs to peer */
|
||||
};
|
||||
|
||||
struct _SeafileSendfsProcClass {
|
||||
CcnetProcessorClass parent_class;
|
||||
};
|
||||
|
||||
GType seafile_sendfs_proc_get_type ();
|
||||
|
||||
#endif
|
||||
|
@ -1,510 +0,0 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#define DEBUG_FLAG SEAFILE_DEBUG_TRANSFER
|
||||
#include "log.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <ccnet.h>
|
||||
#include "utils.h"
|
||||
|
||||
#include "seafile-session.h"
|
||||
#include "commit-mgr.h"
|
||||
#include "fs-mgr.h"
|
||||
#include "processors/objecttx-common.h"
|
||||
#include "sendfs-v2-proc.h"
|
||||
|
||||
#include "diff-simple.h"
|
||||
|
||||
/*
|
||||
* recvfs-v2-proc
|
||||
* ------------------------------>
|
||||
*
|
||||
* OK
|
||||
* <-----------------------------
|
||||
*
|
||||
* Calculate send object list
|
||||
*
|
||||
* SC_OBJ_LIST_SEG
|
||||
* ----------------------------->
|
||||
* SC_OBJ_LIST_SEG
|
||||
* <----------------------------
|
||||
* ......
|
||||
* SC_OBJ_LIST_SEG_END
|
||||
* ----------------------------->
|
||||
*
|
||||
* SC_OBJ
|
||||
* ----------------------------->
|
||||
* ......
|
||||
* After all objects are saved to disk, the server ends the protocol.
|
||||
* SC_END
|
||||
* <----------------------------
|
||||
*/
|
||||
|
||||
enum {
|
||||
INIT = 0,
|
||||
CHECK_OBJECT_LIST,
|
||||
SEND_OBJECTS,
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
GList *send_obj_list;
|
||||
GList *recv_obj_list;
|
||||
|
||||
guint32 reader_id;
|
||||
|
||||
gboolean calc_success;
|
||||
} SeafileSendfsProcPriv;
|
||||
|
||||
#define GET_PRIV(o) \
|
||||
(G_TYPE_INSTANCE_GET_PRIVATE ((o), SEAFILE_TYPE_SENDFS_V2_PROC, SeafileSendfsProcPriv))
|
||||
|
||||
#define USE_PRIV \
|
||||
SeafileSendfsProcPriv *priv = GET_PRIV(processor);
|
||||
|
||||
G_DEFINE_TYPE (SeafileSendfsV2Proc, seafile_sendfs_v2_proc, CCNET_TYPE_PROCESSOR)
|
||||
|
||||
static int start (CcnetProcessor *processor, int argc, char **argv);
|
||||
static void handle_response (CcnetProcessor *processor,
|
||||
char *code, char *code_msg,
|
||||
char *content, int clen);
|
||||
|
||||
static void
|
||||
release_resource(CcnetProcessor *processor)
|
||||
{
|
||||
USE_PRIV;
|
||||
|
||||
string_list_free (priv->send_obj_list);
|
||||
string_list_free (priv->recv_obj_list);
|
||||
|
||||
seaf_obj_store_unregister_async_read (seaf->fs_mgr->obj_store,
|
||||
priv->reader_id);
|
||||
|
||||
CCNET_PROCESSOR_CLASS (seafile_sendfs_v2_proc_parent_class)->release_resource (processor);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
seafile_sendfs_v2_proc_class_init (SeafileSendfsV2ProcClass *klass)
|
||||
{
|
||||
CcnetProcessorClass *proc_class = CCNET_PROCESSOR_CLASS (klass);
|
||||
|
||||
proc_class->name = "sendfs-v2-proc";
|
||||
proc_class->start = start;
|
||||
proc_class->handle_response = handle_response;
|
||||
proc_class->release_resource = release_resource;
|
||||
|
||||
g_type_class_add_private (klass, sizeof(SeafileSendfsProcPriv));
|
||||
}
|
||||
|
||||
static void
|
||||
seafile_sendfs_v2_proc_init (SeafileSendfsV2Proc *processor)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
fs_object_read_cb (OSAsyncResult *res, void *data);
|
||||
|
||||
static int
|
||||
start (CcnetProcessor *processor, int argc, char **argv)
|
||||
{
|
||||
USE_PRIV;
|
||||
GString *buf;
|
||||
SeafileSendfsV2Proc *proc = (SeafileSendfsV2Proc *)processor;
|
||||
TransferTask *task = proc->tx_task;
|
||||
|
||||
buf = g_string_new (NULL);
|
||||
g_string_printf (buf, "remote %s seafile-recvfs-v2 %s",
|
||||
processor->peer_id, task->session_token);
|
||||
ccnet_processor_send_request (processor, buf->str);
|
||||
g_string_free (buf, TRUE);
|
||||
|
||||
priv->reader_id = seaf_obj_store_register_async_read (seaf->fs_mgr->obj_store,
|
||||
task->repo_id,
|
||||
task->repo_version,
|
||||
fs_object_read_cb,
|
||||
processor);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Calculate send object list */
|
||||
|
||||
typedef struct {
|
||||
GList **pret;
|
||||
GHashTable *checked_objs;
|
||||
} CalcData;
|
||||
|
||||
inline static gboolean
|
||||
dirent_same (SeafDirent *denta, SeafDirent *dentb)
|
||||
{
|
||||
return (strcmp (dentb->id, denta->id) == 0 && denta->mode == dentb->mode);
|
||||
}
|
||||
|
||||
static int
|
||||
collect_file_ids (int n, const char *basedir, SeafDirent *files[], void *vdata)
|
||||
{
|
||||
SeafDirent *file1 = files[0];
|
||||
SeafDirent *file2 = files[1];
|
||||
CalcData *data = vdata;
|
||||
GList **pret = data->pret;
|
||||
int dummy;
|
||||
|
||||
if (!file1 || strcmp (file1->id, EMPTY_SHA1) == 0)
|
||||
return 0;
|
||||
|
||||
if (g_hash_table_lookup (data->checked_objs, file1->id))
|
||||
return 0;
|
||||
|
||||
if (!file2 || !dirent_same (file1, file2)) {
|
||||
*pret = g_list_prepend (*pret, g_strdup(file1->id));
|
||||
g_hash_table_insert (data->checked_objs, g_strdup(file1->id), &dummy);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
collect_dir_ids (int n, const char *basedir, SeafDirent *dirs[], void *vdata,
|
||||
gboolean *recurse)
|
||||
{
|
||||
SeafDirent *dir1 = dirs[0];
|
||||
SeafDirent *dir2 = dirs[1];
|
||||
CalcData *data = vdata;
|
||||
GList **pret = data->pret;
|
||||
int dummy;
|
||||
|
||||
if (!dir1 || strcmp (dir1->id, EMPTY_SHA1) == 0)
|
||||
return 0;
|
||||
|
||||
if (g_hash_table_lookup (data->checked_objs, dir1->id))
|
||||
return 0;
|
||||
|
||||
if (!dir2 || !dirent_same (dir1, dir2)) {
|
||||
*pret = g_list_prepend (*pret, g_strdup(dir1->id));
|
||||
g_hash_table_insert (data->checked_objs, g_strdup(dir1->id), &dummy);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *
|
||||
calculate_send_object_list (void *vdata)
|
||||
{
|
||||
CcnetProcessor *processor = vdata;
|
||||
USE_PRIV;
|
||||
SeafileSendfsV2Proc *proc = (SeafileSendfsV2Proc *)processor;
|
||||
TransferTask *task = proc->tx_task;
|
||||
|
||||
SeafBranch *local = NULL, *master = NULL;
|
||||
SeafCommit *local_head = NULL, *master_head = NULL;
|
||||
local = seaf_branch_manager_get_branch (seaf->branch_mgr, task->repo_id, "local");
|
||||
if (!local) {
|
||||
seaf_warning ("Branch local not found for repo %.8s.\n", task->repo_id);
|
||||
priv->calc_success = FALSE;
|
||||
goto out;
|
||||
}
|
||||
master = seaf_branch_manager_get_branch (seaf->branch_mgr, task->repo_id, "master");
|
||||
if (!master) {
|
||||
seaf_warning ("Branch master not found for repo %.8s.\n", task->repo_id);
|
||||
priv->calc_success = FALSE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
local_head = seaf_commit_manager_get_commit (seaf->commit_mgr,
|
||||
task->repo_id, task->repo_version,
|
||||
local->commit_id);
|
||||
if (!local_head) {
|
||||
seaf_warning ("Local head commit not found for repo %.8s.\n",
|
||||
task->repo_id);
|
||||
priv->calc_success = FALSE;
|
||||
goto out;
|
||||
}
|
||||
master_head = seaf_commit_manager_get_commit (seaf->commit_mgr,
|
||||
task->repo_id, task->repo_version,
|
||||
master->commit_id);
|
||||
if (!master_head) {
|
||||
seaf_warning ("Master head commit not found for repo %.8s.\n",
|
||||
task->repo_id);
|
||||
priv->calc_success = FALSE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Diff won't traverse the root object itself. */
|
||||
if (strcmp (local_head->root_id, master_head->root_id) != 0 &&
|
||||
strcmp (local_head->root_id, EMPTY_SHA1) != 0)
|
||||
priv->send_obj_list = g_list_prepend (priv->send_obj_list,
|
||||
g_strdup(local_head->root_id));
|
||||
|
||||
CalcData *data = g_new0(CalcData, 1);
|
||||
data->pret = &priv->send_obj_list;
|
||||
data->checked_objs = g_hash_table_new_full (g_str_hash, g_str_equal,
|
||||
g_free, NULL);
|
||||
|
||||
DiffOptions opts;
|
||||
memset (&opts, 0, sizeof(opts));
|
||||
memcpy (opts.store_id, task->repo_id, 36);
|
||||
opts.version = task->repo_version;
|
||||
opts.file_cb = collect_file_ids;
|
||||
opts.dir_cb = collect_dir_ids;
|
||||
opts.data = data;
|
||||
|
||||
const char *trees[2];
|
||||
trees[0] = local_head->root_id;
|
||||
trees[1] = master_head->root_id;
|
||||
if (diff_trees (2, trees, &opts) < 0) {
|
||||
seaf_warning ("Failed to diff local and master head for repo %.8s.\n",
|
||||
task->repo_id);
|
||||
priv->calc_success = FALSE;
|
||||
}
|
||||
|
||||
g_hash_table_destroy (data->checked_objs);
|
||||
g_free (data);
|
||||
|
||||
priv->calc_success = TRUE;
|
||||
|
||||
out:
|
||||
seaf_branch_unref (local);
|
||||
seaf_branch_unref (master);
|
||||
seaf_commit_unref (local_head);
|
||||
seaf_commit_unref (master_head);
|
||||
return vdata;
|
||||
}
|
||||
|
||||
static void
|
||||
send_object_list_segment (CcnetProcessor *processor);
|
||||
|
||||
static void
|
||||
calculate_send_object_list_done (void *vdata)
|
||||
{
|
||||
CcnetProcessor *processor = vdata;
|
||||
USE_PRIV;
|
||||
|
||||
if (!priv->calc_success) {
|
||||
ccnet_processor_send_update (processor, SC_SHUTDOWN, SS_SHUTDOWN, NULL, 0);
|
||||
ccnet_processor_done (processor, FALSE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (priv->send_obj_list == NULL) {
|
||||
seaf_message ("No fs objects to upload. Done.\n");
|
||||
ccnet_processor_send_update (processor, SC_END, SS_END, NULL, 0);
|
||||
ccnet_processor_done (processor, TRUE);
|
||||
return;
|
||||
}
|
||||
|
||||
send_object_list_segment (processor);
|
||||
}
|
||||
|
||||
/* Check object list. */
|
||||
|
||||
#define OBJECT_LIST_SEGMENT_N 1000
|
||||
#define OBJECT_LIST_SEGMENT_LEN 40 * 1000
|
||||
|
||||
static void
|
||||
send_next_object (CcnetProcessor *processor);
|
||||
|
||||
static void
|
||||
send_object_list_segment (CcnetProcessor *processor)
|
||||
{
|
||||
USE_PRIV;
|
||||
char buf[OBJECT_LIST_SEGMENT_LEN];
|
||||
|
||||
if (priv->send_obj_list == NULL) {
|
||||
seaf_debug ("Check object list end.\n");
|
||||
|
||||
ccnet_processor_send_update (processor,
|
||||
SC_OBJ_LIST_SEG_END, SS_OBJ_LIST_SEG_END,
|
||||
NULL, 0);
|
||||
|
||||
send_next_object (processor);
|
||||
processor->state = SEND_OBJECTS;
|
||||
return;
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
char *p = buf;
|
||||
char *obj_id;
|
||||
while (priv->send_obj_list != NULL) {
|
||||
obj_id = priv->send_obj_list->data;
|
||||
priv->send_obj_list = g_list_delete_link (priv->send_obj_list,
|
||||
priv->send_obj_list);
|
||||
|
||||
memcpy (p, obj_id, 40);
|
||||
p += 40;
|
||||
g_free (obj_id);
|
||||
if (++i == OBJECT_LIST_SEGMENT_N)
|
||||
break;
|
||||
}
|
||||
|
||||
if (i > 0) {
|
||||
seaf_debug ("Send %d object ids.\n", i);
|
||||
ccnet_processor_send_update (processor,
|
||||
SC_OBJ_LIST_SEG, SS_OBJ_LIST_SEG,
|
||||
buf, i * 40);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
process_object_list_segment (CcnetProcessor *processor, char *content, int clen)
|
||||
{
|
||||
USE_PRIV;
|
||||
int n, i;
|
||||
char *p;
|
||||
|
||||
if (clen % 40 != 0) {
|
||||
seaf_warning ("Invalid object list segment length %d.\n", clen);
|
||||
ccnet_processor_send_update (processor, SC_SHUTDOWN, SS_SHUTDOWN, NULL, 0);
|
||||
ccnet_processor_done (processor, FALSE);
|
||||
return;
|
||||
}
|
||||
|
||||
n = clen/40;
|
||||
p = content;
|
||||
|
||||
seaf_debug ("%d objects are needed by the server.\n", n);
|
||||
|
||||
for (i = 0; i < n; ++i) {
|
||||
priv->recv_obj_list = g_list_prepend (priv->recv_obj_list, g_strndup (p, 40));
|
||||
p += 40;
|
||||
}
|
||||
}
|
||||
|
||||
/* Send objects */
|
||||
|
||||
static void
|
||||
send_fs_object (CcnetProcessor *processor,
|
||||
const char *object_id, char *data, int len)
|
||||
{
|
||||
ObjectPack *pack = NULL;
|
||||
int pack_size;
|
||||
|
||||
pack_size = sizeof(ObjectPack) + len;
|
||||
pack = malloc (pack_size);
|
||||
memcpy (pack->id, object_id, 41);
|
||||
memcpy (pack->object, data, len);
|
||||
|
||||
if (pack_size <= MAX_OBJ_SEG_SIZE) {
|
||||
ccnet_processor_send_update (processor, SC_OBJECT, SS_OBJECT,
|
||||
(char *)pack, pack_size);
|
||||
} else {
|
||||
int offset, n;
|
||||
|
||||
offset = 0;
|
||||
while (offset < pack_size) {
|
||||
n = MIN(pack_size - offset, MAX_OBJ_SEG_SIZE);
|
||||
|
||||
if (offset + n < pack_size) {
|
||||
ccnet_processor_send_update (processor,
|
||||
SC_OBJ_SEG, SS_OBJ_SEG,
|
||||
(char *)pack + offset, n);
|
||||
} else {
|
||||
ccnet_processor_send_update (processor,
|
||||
SC_OBJ_SEG_END, SS_OBJ_SEG_END,
|
||||
(char *)pack + offset, n);
|
||||
}
|
||||
|
||||
seaf_debug ("Sent object %s segment<total = %d, offset = %d, n = %d>\n",
|
||||
object_id, pack_size, offset, n);
|
||||
|
||||
offset += n;
|
||||
}
|
||||
}
|
||||
|
||||
seaf_debug ("Send fs object %.8s.\n", object_id);
|
||||
|
||||
free (pack);
|
||||
}
|
||||
|
||||
static void
|
||||
fs_object_read_cb (OSAsyncResult *res, void *data)
|
||||
{
|
||||
CcnetProcessor *processor = data;
|
||||
|
||||
if (!res->success) {
|
||||
seaf_warning ("Failed to read fs object %.8s.\n", res->obj_id);
|
||||
ccnet_processor_send_update (processor, SC_NOT_FOUND, SS_NOT_FOUND,
|
||||
res->obj_id, 41);
|
||||
ccnet_processor_done (processor, FALSE);
|
||||
return;
|
||||
}
|
||||
|
||||
send_fs_object (processor, res->obj_id, res->data, res->len);
|
||||
|
||||
send_next_object (processor);
|
||||
}
|
||||
|
||||
static void
|
||||
read_fs_object (CcnetProcessor *processor, const char *obj_id)
|
||||
{
|
||||
USE_PRIV;
|
||||
|
||||
seaf_obj_store_async_read (seaf->fs_mgr->obj_store,
|
||||
priv->reader_id,
|
||||
obj_id);
|
||||
}
|
||||
|
||||
static void
|
||||
send_next_object (CcnetProcessor *processor)
|
||||
{
|
||||
USE_PRIV;
|
||||
char *object_id;
|
||||
|
||||
if (priv->recv_obj_list == NULL) {
|
||||
seaf_debug ("Send fs objects end.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
object_id = priv->recv_obj_list->data;
|
||||
priv->recv_obj_list = g_list_delete_link (priv->recv_obj_list,
|
||||
priv->recv_obj_list);
|
||||
|
||||
read_fs_object (processor, object_id);
|
||||
g_free (object_id);
|
||||
}
|
||||
|
||||
static void
|
||||
handle_response (CcnetProcessor *processor,
|
||||
char *code, char *code_msg,
|
||||
char *content, int clen)
|
||||
{
|
||||
SeafileSendfsV2Proc *proc = (SeafileSendfsV2Proc *)processor;
|
||||
TransferTask *task = proc->tx_task;
|
||||
|
||||
switch (processor->state) {
|
||||
case INIT:
|
||||
if (strncmp(code, SC_OK, 3) == 0) {
|
||||
ccnet_processor_thread_create (processor, seaf->job_mgr,
|
||||
calculate_send_object_list,
|
||||
calculate_send_object_list_done,
|
||||
processor);
|
||||
processor->state = CHECK_OBJECT_LIST;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case CHECK_OBJECT_LIST:
|
||||
if (strncmp (code, SC_OBJ_LIST_SEG, 3) == 0) {
|
||||
process_object_list_segment (processor, content, clen);
|
||||
send_object_list_segment (processor);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case SEND_OBJECTS:
|
||||
if (strncmp (code, SC_END, 3) == 0) {
|
||||
seaf_debug ("All objects received. Done.\n");
|
||||
ccnet_processor_done (processor, TRUE);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
g_return_if_reached ();
|
||||
}
|
||||
|
||||
seaf_warning ("Bad response: %s %s.\n", code, code_msg);
|
||||
if (memcmp (code, SC_ACCESS_DENIED, 3) == 0)
|
||||
transfer_task_set_error (task, TASK_ERR_ACCESS_DENIED);
|
||||
ccnet_processor_done (processor, FALSE);
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#ifndef SEAFILE_SENDFS_V2_PROC_H
|
||||
#define SEAFILE_SENDFS_V2_PROC_H
|
||||
|
||||
#include <glib-object.h>
|
||||
#include <ccnet/processor.h>
|
||||
#include "transfer-mgr.h"
|
||||
|
||||
#define SEAFILE_TYPE_SENDFS_V2_PROC (seafile_sendfs_v2_proc_get_type ())
|
||||
#define SEAFILE_SENDFS_V2_PROC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SEAFILE_TYPE_SENDFS_V2_PROC, SeafileSendfsV2Proc))
|
||||
#define SEAFILE_IS_SENDFS_V2_PROC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SEAFILE_TYPE_SENDFS_V2_PROC))
|
||||
#define SEAFILE_SENDFS_V2_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SEAFILE_TYPE_SENDFS_V2_PROC, SeafileSendfsV2ProcClass))
|
||||
#define IS_SEAFILE_SENDFS_V2_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SEAFILE_TYPE_SENDFS_V2_PROC))
|
||||
#define SEAFILE_SENDFS_V2_PROC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SEAFILE_TYPE_SENDFS_V2_PROC, SeafileSendfsV2ProcClass))
|
||||
|
||||
typedef struct _SeafileSendfsV2Proc SeafileSendfsV2Proc;
|
||||
typedef struct _SeafileSendfsV2ProcClass SeafileSendfsV2ProcClass;
|
||||
|
||||
struct _SeafileSendfsV2Proc {
|
||||
CcnetProcessor parent_instance;
|
||||
|
||||
TransferTask *tx_task;
|
||||
};
|
||||
|
||||
struct _SeafileSendfsV2ProcClass {
|
||||
CcnetProcessorClass parent_class;
|
||||
};
|
||||
|
||||
GType seafile_sendfs_v2_proc_get_type ();
|
||||
|
||||
#endif
|
||||
|
@ -1,131 +0,0 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include "seafile-session.h"
|
||||
#include "repo-mgr.h"
|
||||
#include "sync-mgr.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include "sync-repo-proc.h"
|
||||
#include "sync-repo-common.h"
|
||||
|
||||
#define DEBUG_FLAG SEAFILE_DEBUG_SYNC
|
||||
#include "log.h"
|
||||
|
||||
/*
|
||||
|
||||
client relay
|
||||
|
||||
sync-repo-slave <repo-id> <branch>
|
||||
---------------------------------->
|
||||
300 <head_commit>
|
||||
<----------------------------------
|
||||
|
||||
or
|
||||
|
||||
301 No such repo
|
||||
<----------------------------------
|
||||
|
||||
302 No such branch
|
||||
<----------------------------------
|
||||
|
||||
*/
|
||||
|
||||
|
||||
G_DEFINE_TYPE (SeafileSyncRepoProc, seafile_sync_repo_proc, CCNET_TYPE_PROCESSOR)
|
||||
|
||||
|
||||
static int
|
||||
sync_repo_start (CcnetProcessor *processor, int argc, char **argv);
|
||||
|
||||
static void
|
||||
handle_response (CcnetProcessor *processor,
|
||||
char *code, char *code_msg,
|
||||
char *content, int clen);
|
||||
|
||||
static void
|
||||
seafile_sync_repo_proc_class_init (SeafileSyncRepoProcClass *klass)
|
||||
{
|
||||
CcnetProcessorClass *proc_class = CCNET_PROCESSOR_CLASS (klass);
|
||||
|
||||
proc_class->name = "seafile-sync-repo";
|
||||
proc_class->start = sync_repo_start;
|
||||
proc_class->handle_response = handle_response;
|
||||
}
|
||||
|
||||
static void
|
||||
seafile_sync_repo_proc_init (SeafileSyncRepoProc *processor)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
sync_repo_start (CcnetProcessor *processor, int argc, char **argv)
|
||||
{
|
||||
SeafileSyncRepoProc *proc = (SeafileSyncRepoProc *) processor;
|
||||
|
||||
if (argc != 0) {
|
||||
seaf_warning ("[sync-repo] argc should be 0.\n");
|
||||
ccnet_processor_done (processor, FALSE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!proc->task) {
|
||||
seaf_warning ("[sync-repo] Error: not provide info task.\n");
|
||||
ccnet_processor_done (processor, FALSE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
char buf[256];
|
||||
|
||||
/* Use a virutal "fetch_head" branch that works both on client and server. */
|
||||
snprintf (buf, 256, "remote %s seafile-sync-repo-slave %s %s",
|
||||
processor->peer_id, proc->task->info->repo_id, "fetch_head");
|
||||
|
||||
ccnet_processor_send_request (processor, buf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void handle_response (CcnetProcessor *processor,
|
||||
char *code, char *code_msg,
|
||||
char *content, int clen)
|
||||
{
|
||||
SeafileSyncRepoProc *proc = (SeafileSyncRepoProc *)processor;
|
||||
|
||||
proc->task->info->deleted_on_relay = FALSE;
|
||||
proc->task->info->branch_deleted_on_relay = FALSE;
|
||||
proc->task->info->repo_corrupted = FALSE;
|
||||
|
||||
if (memcmp (code, SC_COMMIT_ID, 3) == 0) {
|
||||
|
||||
if (content[clen-1] != '\0') {
|
||||
seaf_warning ("[sync-repo] Response not end with NULL\n");
|
||||
ccnet_processor_done (processor, FALSE);
|
||||
return;
|
||||
}
|
||||
|
||||
/* g_debug ("[sync-repo] Get repo head commit %s\n", content); */
|
||||
if (strlen(content) != 40) {
|
||||
seaf_debug ("[sync-repo] Invalid commit id\n");
|
||||
ccnet_processor_done (processor, FALSE);
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(proc->task->info->head_commit, content, 41);
|
||||
|
||||
ccnet_processor_done (processor, TRUE);
|
||||
} else if (memcmp (code, SC_NO_REPO, 3) == 0) {
|
||||
proc->task->info->deleted_on_relay = TRUE;
|
||||
ccnet_processor_done (processor, TRUE);
|
||||
} else if (memcmp (code, SC_NO_BRANCH, 3) == 0) {
|
||||
proc->task->info->branch_deleted_on_relay = TRUE;
|
||||
ccnet_processor_done (processor, TRUE);
|
||||
} else if (memcmp (code, SC_REPO_CORRUPT, 3) == 0) {
|
||||
proc->task->info->repo_corrupted = TRUE;
|
||||
ccnet_processor_done (processor, TRUE);
|
||||
} else
|
||||
ccnet_processor_done (processor, FALSE);
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#ifndef SEAFILE_SYNC_REPO_PROC_H
|
||||
#define SEAFILE_SYNC_REPO_PROC_H
|
||||
|
||||
#include <glib-object.h>
|
||||
#include <ccnet.h>
|
||||
|
||||
#define SEAFILE_TYPE_SYNC_REPO_PROC (seafile_sync_repo_proc_get_type ())
|
||||
#define SEAFILE_SYNC_REPO_PROC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SEAFILE_TYPE_SYNC_REPO_PROC, SeafileSyncRepoProc))
|
||||
#define SEAFILE_IS_SYNC_REPO_PROC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SEAFILE_TYPE_SYNC_REPO_PROC))
|
||||
#define SEAFILE_SYNC_REPO_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SEAFILE_TYPE_SYNC_REPO_PROC, SeafileSyncRepoProcClass))
|
||||
#define IS_SEAFILE_SYNC_REPO_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SEAFILE_TYPE_SYNC_REPO_PROC))
|
||||
#define SEAFILE_SYNC_REPO_PROC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SEAFILE_TYPE_SYNC_REPO_PROC, SeafileSyncRepoProcClass))
|
||||
|
||||
typedef struct _SeafileSyncRepoProc SeafileSyncRepoProc;
|
||||
typedef struct _SeafileSyncRepoProcClass SeafileSyncRepoProcClass;
|
||||
|
||||
struct _SyncTask;
|
||||
|
||||
struct _SeafileSyncRepoProc {
|
||||
CcnetProcessor parent_instance;
|
||||
|
||||
struct _SyncTask *task;
|
||||
};
|
||||
|
||||
struct _SeafileSyncRepoProcClass {
|
||||
CcnetProcessorClass parent_class;
|
||||
};
|
||||
|
||||
GType seafile_sync_repo_proc_get_type ();
|
||||
|
||||
void
|
||||
seafile_sync_repo_proc_set_repo (SeafileSyncRepoProc *processor,
|
||||
char *repo_id);
|
||||
|
||||
#endif
|
1652
daemon/repo-mgr.c
1652
daemon/repo-mgr.c
File diff suppressed because it is too large
Load Diff
@ -141,55 +141,12 @@ seaf_repo_set_name (SeafRepo *repo, const char *new_name);
|
||||
GList *
|
||||
seaf_repo_get_commits (SeafRepo *repo);
|
||||
|
||||
int
|
||||
seaf_repo_index_add (SeafRepo *repo, const char *path);
|
||||
|
||||
int
|
||||
seaf_repo_index_worktree_files (const char *repo_id,
|
||||
int version,
|
||||
const char *modifier,
|
||||
const char *worktree,
|
||||
const char *passwd,
|
||||
int enc_version,
|
||||
const char *random_key,
|
||||
char *root_id);
|
||||
|
||||
int
|
||||
seaf_repo_index_rm (SeafRepo *repo, const char *path);
|
||||
|
||||
char *
|
||||
seaf_repo_status (SeafRepo *repo);
|
||||
|
||||
gboolean
|
||||
seaf_repo_is_worktree_changed (SeafRepo *repo);
|
||||
|
||||
gboolean
|
||||
seaf_repo_is_index_unmerged (SeafRepo *repo);
|
||||
|
||||
char *
|
||||
seaf_repo_index_commit (SeafRepo *repo, const char *desc,
|
||||
gboolean is_force_commit,
|
||||
gboolean is_initial_commit,
|
||||
GError **error);
|
||||
|
||||
int
|
||||
seaf_repo_checkout (SeafRepo *repo, const char *worktree_parent, char **error);
|
||||
|
||||
int
|
||||
seaf_repo_checkout_commit (SeafRepo *repo, SeafCommit *commit, gboolean recover_merge,
|
||||
char **error);
|
||||
|
||||
enum {
|
||||
MERGE_STATUS_UNKNOWN = 0,
|
||||
MERGE_STATUS_UPTODATE,
|
||||
MERGE_STATUS_FAST_FORWARD,
|
||||
MERGE_STATUS_REAL_MERGE,
|
||||
};
|
||||
|
||||
int
|
||||
seaf_repo_merge (SeafRepo *repo, const char *branch, char **error,
|
||||
int *merge_status);
|
||||
|
||||
GList *
|
||||
seaf_repo_diff (SeafRepo *repo, const char *old, const char *new, int fold_dir_diff, char **error);
|
||||
|
||||
@ -321,68 +278,6 @@ seaf_repo_manager_set_repo_passwd (SeafRepoManager *manager,
|
||||
SeafRepo *repo,
|
||||
const char *passwd);
|
||||
|
||||
int
|
||||
seaf_repo_manager_set_repo_relay_id (SeafRepoManager *mgr,
|
||||
SeafRepo *repo,
|
||||
const char *relay_id);
|
||||
|
||||
int
|
||||
seaf_repo_manager_set_merge (SeafRepoManager *manager,
|
||||
const char *repo_id,
|
||||
const char *remote_head);
|
||||
|
||||
int
|
||||
seaf_repo_manager_clear_merge (SeafRepoManager *manager,
|
||||
const char *repo_id);
|
||||
|
||||
typedef struct {
|
||||
gboolean in_merge;
|
||||
char remote_head[41];
|
||||
} SeafRepoMergeInfo;
|
||||
|
||||
int
|
||||
seaf_repo_manager_get_merge_info (SeafRepoManager *manager,
|
||||
const char *repo_id,
|
||||
SeafRepoMergeInfo *info);
|
||||
|
||||
int
|
||||
seaf_repo_manager_get_common_ancestor (SeafRepoManager *manager,
|
||||
const char *repo_id,
|
||||
char *common_ancestor,
|
||||
char *head_id);
|
||||
|
||||
int
|
||||
seaf_repo_manager_set_common_ancestor (SeafRepoManager *manager,
|
||||
const char *repo_id,
|
||||
const char *common_ancestor,
|
||||
const char *head_id);
|
||||
|
||||
typedef struct {
|
||||
char repo_id[41];
|
||||
char worktree[SEAF_PATH_MAX];
|
||||
int total_files;
|
||||
int finished_files;
|
||||
gboolean success;
|
||||
} CheckoutTask;
|
||||
|
||||
typedef void (*CheckoutDoneCallback) (CheckoutTask *, SeafRepo *, void *);
|
||||
|
||||
int
|
||||
seaf_repo_manager_add_checkout_task (SeafRepoManager *mgr,
|
||||
SeafRepo *repo,
|
||||
const char *worktree,
|
||||
CheckoutDoneCallback done_cb,
|
||||
void *cb_data);
|
||||
|
||||
CheckoutTask *
|
||||
seaf_repo_manager_get_checkout_task (SeafRepoManager *mgr,
|
||||
const char *repo_id);
|
||||
int
|
||||
seaf_repo_manager_update_repo_relay_info (SeafRepoManager *mgr,
|
||||
SeafRepo *repo,
|
||||
const char *new_addr,
|
||||
const char *new_port);
|
||||
|
||||
int
|
||||
seaf_repo_manager_update_repos_server_host (SeafRepoManager *mgr,
|
||||
const char *old_host,
|
||||
@ -427,9 +322,7 @@ struct _TransferTask;
|
||||
struct _HttpTxTask;
|
||||
|
||||
int
|
||||
seaf_repo_fetch_and_checkout (struct _TransferTask *task,
|
||||
struct _HttpTxTask *http_task,
|
||||
gboolean is_http,
|
||||
seaf_repo_fetch_and_checkout (struct _HttpTxTask *http_task,
|
||||
const char *remote_head_id);
|
||||
|
||||
gboolean
|
||||
|
@ -21,14 +21,11 @@
|
||||
#include <c_bpwrapper.h>
|
||||
#endif // HAVE_BREAKPAD_SUPPORT
|
||||
|
||||
#include <ccnet.h>
|
||||
#include <searpc-server.h>
|
||||
#include <searpc-client.h>
|
||||
#include <searpc.h>
|
||||
#include <searpc-named-pipe-transport.h>
|
||||
|
||||
#include "seafile-session.h"
|
||||
#include "seafile-rpc.h"
|
||||
#include <ccnet/rpcserver-proc.h>
|
||||
#include <ccnet/threaded-rpcserver-proc.h>
|
||||
#include "log.h"
|
||||
#include "utils.h"
|
||||
#include "vc-utils.h"
|
||||
@ -45,9 +42,6 @@
|
||||
|
||||
|
||||
SeafileSession *seaf;
|
||||
SearpcClient *ccnetrpc_client;
|
||||
SearpcClient *appletrpc_client;
|
||||
CcnetClient *bind_client;
|
||||
|
||||
static const char *short_options = "hvc:d:w:l:D:bg:G:";
|
||||
static struct option long_options[] = {
|
||||
@ -73,18 +67,15 @@ static void usage ()
|
||||
#include "searpc-signature.h"
|
||||
#include "searpc-marshal.h"
|
||||
|
||||
#define SEAFILE_SOCKET_NAME "seafile.sock"
|
||||
|
||||
static void
|
||||
start_rpc_service (CcnetClient *client)
|
||||
register_rpc_service ()
|
||||
{
|
||||
searpc_server_init (register_marshals);
|
||||
|
||||
searpc_create_service ("seafile-rpcserver");
|
||||
ccnet_register_service (client, "seafile-rpcserver", "rpc-inner",
|
||||
CCNET_TYPE_RPCSERVER_PROC, NULL);
|
||||
|
||||
searpc_create_service ("seafile-threaded-rpcserver");
|
||||
ccnet_register_service (client, "seafile-threaded-rpcserver", "rpc-inner",
|
||||
CCNET_TYPE_THREADED_RPCSERVER_PROC, NULL);
|
||||
|
||||
/* seafile-rpcserver */
|
||||
searpc_server_register_function ("seafile-rpcserver",
|
||||
@ -161,21 +152,6 @@ start_rpc_service (CcnetClient *client)
|
||||
"seafile_get_repo_property",
|
||||
searpc_signature_string__string_string());
|
||||
|
||||
searpc_server_register_function ("seafile-rpcserver",
|
||||
seafile_get_repo_relay_address,
|
||||
"seafile_get_repo_relay_address",
|
||||
searpc_signature_string__string());
|
||||
|
||||
searpc_server_register_function ("seafile-rpcserver",
|
||||
seafile_get_repo_relay_port,
|
||||
"seafile_get_repo_relay_port",
|
||||
searpc_signature_string__string());
|
||||
|
||||
searpc_server_register_function ("seafile-rpcserver",
|
||||
seafile_update_repo_relay_info,
|
||||
"seafile_update_repo_relay_info",
|
||||
searpc_signature_int__string_string_string());
|
||||
|
||||
searpc_server_register_function ("seafile-rpcserver",
|
||||
seafile_update_repos_server_host,
|
||||
"seafile_update_repos_server_host",
|
||||
@ -196,10 +172,6 @@ start_rpc_service (CcnetClient *client)
|
||||
"seafile_is_auto_sync_enabled",
|
||||
searpc_signature_int__void());
|
||||
|
||||
searpc_server_register_function ("seafile-rpcserver",
|
||||
seafile_branch_gets,
|
||||
"seafile_branch_gets",
|
||||
searpc_signature_objlist__string());
|
||||
searpc_server_register_function ("seafile-rpcserver",
|
||||
seafile_gen_default_worktree,
|
||||
"gen_default_worktree",
|
||||
@ -208,7 +180,7 @@ start_rpc_service (CcnetClient *client)
|
||||
seafile_check_path_for_clone,
|
||||
"seafile_check_path_for_clone",
|
||||
searpc_signature_int__string());
|
||||
|
||||
|
||||
/* clone means sync with existing folder, download means sync to a new folder. */
|
||||
searpc_server_register_function ("seafile-rpcserver",
|
||||
seafile_clone,
|
||||
@ -244,11 +216,6 @@ start_rpc_service (CcnetClient *client)
|
||||
"seafile_get_repo",
|
||||
searpc_signature_object__string());
|
||||
|
||||
searpc_server_register_function ("seafile-rpcserver",
|
||||
seafile_get_sync_task_list,
|
||||
"seafile_get_sync_task_list",
|
||||
searpc_signature_objlist__void());
|
||||
|
||||
searpc_server_register_function ("seafile-rpcserver",
|
||||
seafile_get_repo_sync_task,
|
||||
"seafile_get_repo_sync_task",
|
||||
@ -259,25 +226,6 @@ start_rpc_service (CcnetClient *client)
|
||||
"seafile_get_repo_sync_info",
|
||||
searpc_signature_object__string());
|
||||
|
||||
searpc_server_register_function ("seafile-rpcserver",
|
||||
seafile_get_commit,
|
||||
"seafile_get_commit",
|
||||
searpc_signature_object__string_int_string());
|
||||
searpc_server_register_function ("seafile-rpcserver",
|
||||
seafile_get_commit_list,
|
||||
"seafile_get_commit_list",
|
||||
searpc_signature_objlist__string_int_int());
|
||||
|
||||
|
||||
searpc_server_register_function ("seafile-rpcserver",
|
||||
seafile_find_transfer_task,
|
||||
"seafile_find_transfer_task",
|
||||
searpc_signature_object__string());
|
||||
searpc_server_register_function ("seafile-rpcserver",
|
||||
seafile_get_checkout_task,
|
||||
"seafile_get_checkout_task",
|
||||
searpc_signature_object__string());
|
||||
|
||||
searpc_server_register_function ("seafile-rpcserver",
|
||||
seafile_get_path_sync_status,
|
||||
"seafile_get_path_sync_status",
|
||||
@ -313,6 +261,11 @@ start_rpc_service (CcnetClient *client)
|
||||
"seafile_get_file_sync_errors",
|
||||
searpc_signature_objlist__int_int());
|
||||
|
||||
searpc_server_register_function ("seafile-rpcserver",
|
||||
seafile_get_sync_notification,
|
||||
"seafile_get_sync_notification",
|
||||
searpc_signature_json__void());
|
||||
|
||||
/* Need to run in a thread since diff may take long. */
|
||||
searpc_server_register_function ("seafile-threaded-rpcserver",
|
||||
seafile_diff,
|
||||
@ -320,34 +273,45 @@ start_rpc_service (CcnetClient *client)
|
||||
searpc_signature_objlist__string_string_string_int());
|
||||
}
|
||||
|
||||
static int
|
||||
start_searpc_server ()
|
||||
{
|
||||
register_rpc_service ();
|
||||
|
||||
#ifdef WIN32
|
||||
DWORD bufCharCount = 32767;
|
||||
char userNameBuf[bufCharCount];
|
||||
if (GetUserName(userNameBuf, &bufCharCount) == 0) {
|
||||
seaf_warning ("Failed to get user name, GLE=%lu, required size is %lu\n",
|
||||
GetLastError(), bufCharCount);
|
||||
return -1;
|
||||
}
|
||||
|
||||
char *path = g_strdup_printf("\\\\.\\pipe\\seafile_%s", userNameBuf);
|
||||
#else
|
||||
char *path = g_build_filename (seaf->seaf_dir, SEAFILE_SOCKET_NAME, NULL);
|
||||
#endif
|
||||
|
||||
SearpcNamedPipeServer *server = searpc_create_named_pipe_server (path);
|
||||
if (!server) {
|
||||
seaf_warning ("Failed to create named pipe server.\n");
|
||||
g_free (path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
seaf->rpc_socket_path = path;
|
||||
|
||||
return searpc_named_pipe_server_start (server);
|
||||
}
|
||||
|
||||
|
||||
#ifndef WIN32
|
||||
static void
|
||||
set_signal_handlers (SeafileSession *session)
|
||||
{
|
||||
#ifndef WIN32
|
||||
signal (SIGPIPE, SIG_IGN);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
create_sync_rpc_clients (const char *config_dir)
|
||||
{
|
||||
CcnetClient *sync_client;
|
||||
|
||||
/* sync client and rpc client */
|
||||
sync_client = ccnet_client_new ();
|
||||
if ( (ccnet_client_load_confdir(sync_client, NULL, config_dir)) < 0 ) {
|
||||
seaf_warning ("Read config dir error\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (ccnet_client_connect_daemon (sync_client, CCNET_CLIENT_SYNC) < 0)
|
||||
{
|
||||
seaf_warning ("Connect to server fail: %s\n", strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
ccnetrpc_client = ccnet_create_rpc_client (sync_client, NULL, "ccnet-rpcserver");
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
/* Get the commandline arguments in unicode, then convert them to utf8 */
|
||||
@ -360,7 +324,7 @@ get_argv_utf8 (int *argc)
|
||||
wchar_t **argv_w = NULL;
|
||||
|
||||
cmdline = GetCommandLineW();
|
||||
argv_w = CommandLineToArgvW (cmdline, argc);
|
||||
argv_w = CommandLineToArgvW (cmdline, argc);
|
||||
if (!argv_w) {
|
||||
printf("failed to CommandLineToArgvW(), GLE=%lu\n", GetLastError());
|
||||
return NULL;
|
||||
@ -375,35 +339,6 @@ get_argv_utf8 (int *argc)
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Bind to an unused service to make sure only one instance of seaf-daemon
|
||||
* is running.
|
||||
*/
|
||||
static gboolean
|
||||
bind_ccnet_service (const char *config_dir)
|
||||
{
|
||||
gboolean ret = TRUE;
|
||||
|
||||
bind_client = ccnet_client_new ();
|
||||
if ( (ccnet_client_load_confdir(bind_client, NULL, config_dir)) < 0 ) {
|
||||
seaf_warning ("Read config dir error\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (ccnet_client_connect_daemon (bind_client, CCNET_CLIENT_SYNC) < 0)
|
||||
{
|
||||
seaf_warning ("Connect to server fail: %s\n", strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (!ccnet_register_service_sync (bind_client,
|
||||
"seafile-dummy-service",
|
||||
"rpc-inner"))
|
||||
ret = FALSE;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
@ -417,6 +352,13 @@ main (int argc, char **argv)
|
||||
checkdir_with_mkdir(dump_dir);
|
||||
CBPWrapperExceptionHandler bp_exception_handler = newCBPWrapperExceptionHandler(dump_dir);
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#define DEFAULT_CONFIG_DIR "~/ccnet"
|
||||
#else
|
||||
#define DEFAULT_CONFIG_DIR "~/.ccnet"
|
||||
#endif
|
||||
|
||||
int c;
|
||||
char *config_dir = DEFAULT_CONFIG_DIR;
|
||||
char *seafile_dir = NULL;
|
||||
@ -424,7 +366,6 @@ main (int argc, char **argv)
|
||||
char *logfile = NULL;
|
||||
const char *debug_str = NULL;
|
||||
int daemon_mode = 0;
|
||||
CcnetClient *client;
|
||||
char *ccnet_debug_level_str = "info";
|
||||
char *seafile_debug_level_str = "debug";
|
||||
|
||||
@ -434,7 +375,7 @@ main (int argc, char **argv)
|
||||
argv = get_argv_utf8 (&argc);
|
||||
#endif
|
||||
|
||||
while ((c = getopt_long (argc, argv, short_options,
|
||||
while ((c = getopt_long (argc, argv, short_options,
|
||||
long_options, NULL)) != EOF)
|
||||
{
|
||||
switch (c) {
|
||||
@ -525,35 +466,17 @@ main (int argc, char **argv)
|
||||
exit (1);
|
||||
}
|
||||
|
||||
if (!bind_ccnet_service (config_dir)) {
|
||||
seaf_warning ("Failed to bind ccnet service\n");
|
||||
exit (1);
|
||||
}
|
||||
|
||||
/* init ccnet */
|
||||
client = ccnet_init (NULL, config_dir);
|
||||
if (!client)
|
||||
exit (1);
|
||||
|
||||
start_rpc_service (client);
|
||||
|
||||
create_sync_rpc_clients (config_dir);
|
||||
appletrpc_client = ccnet_create_async_rpc_client (client, NULL,
|
||||
"applet-rpcserver");
|
||||
|
||||
/* init seafile */
|
||||
if (seafile_dir == NULL)
|
||||
seafile_dir = g_build_filename (config_dir, "seafile-data", NULL);
|
||||
if (worktree_dir == NULL)
|
||||
worktree_dir = g_build_filename (g_get_home_dir(), "seafile", NULL);
|
||||
|
||||
seaf = seafile_session_new (seafile_dir, worktree_dir, client);
|
||||
seaf = seafile_session_new (seafile_dir, worktree_dir, config_dir);
|
||||
if (!seaf) {
|
||||
seaf_warning ("Failed to create seafile session.\n");
|
||||
exit (1);
|
||||
}
|
||||
seaf->ccnetrpc_client = ccnetrpc_client;
|
||||
seaf->appletrpc_client = appletrpc_client;
|
||||
|
||||
seaf_message ("starting seafile client "SEAFILE_CLIENT_VERSION"\n");
|
||||
#if defined(SEAFILE_SOURCE_COMMIT_ID)
|
||||
@ -564,7 +487,12 @@ main (int argc, char **argv)
|
||||
g_free (worktree_dir);
|
||||
g_free (logfile);
|
||||
|
||||
#ifndef WIN32
|
||||
set_signal_handlers (seaf);
|
||||
#else
|
||||
WSADATA wsadata;
|
||||
WSAStartup (0x0101, &wsadata);
|
||||
#endif
|
||||
|
||||
#ifndef USE_GPL_CRYPTO
|
||||
seafile_curl_init();
|
||||
@ -572,8 +500,18 @@ main (int argc, char **argv)
|
||||
seafile_session_prepare (seaf);
|
||||
seafile_session_start (seaf);
|
||||
|
||||
if (start_searpc_server () < 0) {
|
||||
seaf_warning ("Failed to start searpc server.\n");
|
||||
exit (1);
|
||||
}
|
||||
|
||||
seaf_message ("rpc server started.\n");
|
||||
|
||||
|
||||
seafile_session_config_set_string (seaf, "wktree", seaf->worktree_dir);
|
||||
ccnet_main (client);
|
||||
|
||||
event_base_loop (seaf->ev_base, 0);
|
||||
|
||||
#ifndef USE_GPL_CRYPTO
|
||||
seafile_curl_deinit();
|
||||
#endif
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include "seafile-session.h"
|
||||
#include "db.h"
|
||||
|
||||
#define KEY_CLIENT_ID "client_id"
|
||||
#define KEY_CLIENT_NAME "client_name"
|
||||
|
||||
#define KEY_MONITOR_ID "monitor_id"
|
||||
|
@ -14,17 +14,21 @@
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
|
||||
#include <event2/event.h>
|
||||
#include <event2/event_compat.h>
|
||||
#include <event2/event_struct.h>
|
||||
#else
|
||||
#include <event.h>
|
||||
#endif
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#include <ccnet.h>
|
||||
#include <ccnet/cevent.h>
|
||||
#include <ccnet/ccnet-object.h>
|
||||
#include <utils.h>
|
||||
#include "utils.h"
|
||||
|
||||
#include "seafile-session.h"
|
||||
#include "seafile-config.h"
|
||||
#include "vc-utils.h"
|
||||
#include "seaf-utils.h"
|
||||
#include "log.h"
|
||||
|
||||
#define MAX_THREADS 50
|
||||
@ -134,23 +138,20 @@ out:
|
||||
SeafileSession *
|
||||
seafile_session_new(const char *seafile_dir,
|
||||
const char *worktree_dir,
|
||||
struct _CcnetClient *ccnet_session)
|
||||
const char *ccnet_dir)
|
||||
{
|
||||
char *abs_seafile_dir;
|
||||
char *abs_worktree_dir;
|
||||
char *abs_ccnet_dir;
|
||||
char *tmp_file_dir;
|
||||
char *db_path;
|
||||
char *deleted_store;
|
||||
sqlite3 *config_db;
|
||||
SeafileSession *session = NULL;
|
||||
|
||||
#ifndef SEAF_TOOL
|
||||
if (!ccnet_session)
|
||||
return NULL;
|
||||
#endif
|
||||
|
||||
abs_worktree_dir = ccnet_expand_path (worktree_dir);
|
||||
abs_seafile_dir = ccnet_expand_path (seafile_dir);
|
||||
abs_ccnet_dir = ccnet_expand_path (ccnet_dir);
|
||||
tmp_file_dir = g_build_filename (abs_seafile_dir, "tmpfiles", NULL);
|
||||
db_path = g_build_filename (abs_seafile_dir, "config.db", NULL);
|
||||
deleted_store = g_build_filename (abs_seafile_dir, "deleted_store", NULL);
|
||||
@ -183,10 +184,11 @@ seafile_session_new(const char *seafile_dir,
|
||||
}
|
||||
|
||||
session = g_object_new (SEAFILE_TYPE_SESSION, NULL);
|
||||
session->ev_base = event_base_new ();
|
||||
session->seaf_dir = abs_seafile_dir;
|
||||
session->tmp_file_dir = tmp_file_dir;
|
||||
session->worktree_dir = abs_worktree_dir;
|
||||
session->session = ccnet_session;
|
||||
session->ccnet_dir = abs_ccnet_dir;
|
||||
session->config_db = config_db;
|
||||
session->deleted_store = deleted_store;
|
||||
|
||||
@ -206,9 +208,6 @@ seafile_session_new(const char *seafile_dir,
|
||||
if (!session->branch_mgr)
|
||||
goto onerror;
|
||||
|
||||
session->transfer_mgr = seaf_transfer_manager_new (session);
|
||||
if (!session->transfer_mgr)
|
||||
goto onerror;
|
||||
session->clone_mgr = seaf_clone_manager_new (session);
|
||||
if (!session->clone_mgr)
|
||||
goto onerror;
|
||||
@ -226,13 +225,12 @@ seafile_session_new(const char *seafile_dir,
|
||||
if (!session->filelock_mgr)
|
||||
goto onerror;
|
||||
|
||||
session->job_mgr = ccnet_job_manager_new (MAX_THREADS);
|
||||
ccnet_session->job_mgr = ccnet_job_manager_new (MAX_THREADS);
|
||||
session->job_mgr = seaf_job_manager_new (session, MAX_THREADS);
|
||||
session->ev_mgr = cevent_manager_new ();
|
||||
if (!session->ev_mgr)
|
||||
goto onerror;
|
||||
|
||||
session->mq_mgr = seaf_mq_manager_new (session);
|
||||
session->mq_mgr = seaf_mq_manager_new ();
|
||||
if (!session->mq_mgr)
|
||||
goto onerror;
|
||||
|
||||
@ -241,6 +239,7 @@ seafile_session_new(const char *seafile_dir,
|
||||
onerror:
|
||||
free (abs_seafile_dir);
|
||||
free (abs_worktree_dir);
|
||||
free (abs_ccnet_dir);
|
||||
g_free (tmp_file_dir);
|
||||
g_free (db_path);
|
||||
g_free (deleted_store);
|
||||
@ -298,15 +297,91 @@ out:
|
||||
json_decref(json);
|
||||
}
|
||||
|
||||
static char *
|
||||
generate_client_id ()
|
||||
{
|
||||
char *uuid = gen_uuid();
|
||||
unsigned char buf[20];
|
||||
char sha1[41];
|
||||
|
||||
calculate_sha1 (buf, uuid, 20);
|
||||
rawdata_to_hex (buf, sha1, 20);
|
||||
|
||||
g_free (uuid);
|
||||
return g_strdup(sha1);
|
||||
}
|
||||
|
||||
static void
|
||||
read_ccnet_conf (const char *ccnet_dir, char **client_id, char **client_name)
|
||||
{
|
||||
char *ccnet_conf_path = g_build_path ("/", ccnet_dir, "ccnet.conf", NULL);
|
||||
GKeyFile *key_file = g_key_file_new ();
|
||||
GError *error = NULL;
|
||||
|
||||
if (!g_file_test (ccnet_conf_path, G_FILE_TEST_IS_REGULAR))
|
||||
goto out;
|
||||
|
||||
if (!g_key_file_load_from_file (key_file, ccnet_conf_path, 0, &error)) {
|
||||
seaf_warning ("Failed to read ccnet.conf: %s.\n", error->message);
|
||||
g_clear_error (&error);
|
||||
goto out;
|
||||
}
|
||||
|
||||
*client_id = g_key_file_get_string (key_file, "General", "ID", &error);
|
||||
if (error) {
|
||||
seaf_warning ("Failed to read client id from ccnet.conf: %s.\n", error->message);
|
||||
g_clear_error (&error);
|
||||
goto out;
|
||||
}
|
||||
|
||||
*client_name = g_key_file_get_string (key_file, "General", "NAME", &error);
|
||||
if (error) {
|
||||
seaf_warning ("Failed to read client name from ccnet.conf: %s.\n", error->message);
|
||||
g_clear_error (&error);
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
g_free (ccnet_conf_path);
|
||||
g_key_file_free (key_file);
|
||||
}
|
||||
|
||||
void
|
||||
seafile_session_prepare (SeafileSession *session)
|
||||
{
|
||||
session->client_name = seafile_session_config_get_string (session, KEY_CLIENT_NAME);
|
||||
if (!session->client_name) {
|
||||
session->client_name = g_strdup(session->session->base.name);
|
||||
}
|
||||
char *client_id = NULL, *client_name = NULL;
|
||||
|
||||
/* load config */
|
||||
|
||||
read_ccnet_conf (session->ccnet_dir, &client_id, &client_name);
|
||||
|
||||
session->client_id = seafile_session_config_get_string (session, KEY_CLIENT_ID);
|
||||
if (!session->client_id) {
|
||||
if (client_id) {
|
||||
session->client_id = g_strdup (client_id);
|
||||
} else {
|
||||
session->client_id = generate_client_id();
|
||||
}
|
||||
seafile_session_config_set_string (session,
|
||||
KEY_CLIENT_ID,
|
||||
session->client_id);
|
||||
}
|
||||
|
||||
session->client_name = seafile_session_config_get_string (session, KEY_CLIENT_NAME);
|
||||
if (!session->client_name) {
|
||||
if (client_name) {
|
||||
session->client_name = g_strdup (client_name);
|
||||
seafile_session_config_set_string (session,
|
||||
KEY_CLIENT_NAME,
|
||||
session->client_name);
|
||||
} else {
|
||||
session->client_name = g_strdup("unknown");
|
||||
}
|
||||
}
|
||||
|
||||
g_free (client_id);
|
||||
g_free (client_name);
|
||||
|
||||
session->sync_extra_temp_file = seafile_session_config_get_bool
|
||||
(session, KEY_SYNC_EXTRA_TEMP_FILE);
|
||||
|
||||
@ -349,8 +424,6 @@ seafile_session_prepare (SeafileSession *session)
|
||||
#ifndef SEAF_TOOL
|
||||
seaf_sync_manager_init (session->sync_mgr);
|
||||
#endif
|
||||
seaf_mq_manager_set_heartbeat_name (session->mq_mgr,
|
||||
"seafile.heartbeat");
|
||||
}
|
||||
|
||||
/* static void */
|
||||
@ -456,11 +529,6 @@ cleanup_job_done (void *vdata)
|
||||
return;
|
||||
}
|
||||
|
||||
if (seaf_transfer_manager_start (session->transfer_mgr) < 0) {
|
||||
g_error ("Failed to start transfer manager.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (http_tx_manager_start (session->http_tx_mgr) < 0) {
|
||||
g_error ("Failed to start http transfer manager.\n");
|
||||
return;
|
||||
@ -499,45 +567,16 @@ cleanup_job_done (void *vdata)
|
||||
static void
|
||||
on_start_cleanup (SeafileSession *session)
|
||||
{
|
||||
ccnet_job_manager_schedule_job (seaf->job_mgr,
|
||||
on_start_cleanup_job,
|
||||
cleanup_job_done,
|
||||
session);
|
||||
seaf_job_manager_schedule_job (seaf->job_mgr,
|
||||
on_start_cleanup_job,
|
||||
cleanup_job_done,
|
||||
session);
|
||||
}
|
||||
|
||||
void
|
||||
seafile_session_start (SeafileSession *session)
|
||||
{
|
||||
/* MQ must be started to send heartbeat message to applet. */
|
||||
if (seaf_mq_manager_start (session->mq_mgr) < 0) {
|
||||
g_error ("Failed to start mq manager.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Finish cleanup task before anything is run. */
|
||||
on_start_cleanup (session);
|
||||
}
|
||||
|
||||
#if 0
|
||||
void
|
||||
seafile_session_add_event (SeafileSession *session,
|
||||
const char *type,
|
||||
const char *first, ...)
|
||||
{
|
||||
gchar *body;
|
||||
va_list args;
|
||||
|
||||
va_start (args, first);
|
||||
body = key_value_list_to_json_v (first, args);
|
||||
va_end (args);
|
||||
|
||||
CcnetEvent *event = g_object_new (CCNET_TYPE_EVENT,
|
||||
"etype", type,
|
||||
"body", body, NULL);
|
||||
ccnet_client_send_event(session->session, (GObject *)event);
|
||||
g_object_unref (event);
|
||||
|
||||
g_free (body);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -4,9 +4,8 @@
|
||||
#define SEAFILE_SESSION_H
|
||||
|
||||
#include <glib-object.h>
|
||||
#include <ccnet/cevent.h>
|
||||
#include <ccnet/mqclient-proc.h>
|
||||
#include <ccnet/job-mgr.h>
|
||||
#include "cevent.h"
|
||||
#include "job-mgr.h"
|
||||
|
||||
#include "block-mgr.h"
|
||||
#include "fs-mgr.h"
|
||||
@ -16,7 +15,6 @@
|
||||
#include "clone-mgr.h"
|
||||
#include "db.h"
|
||||
|
||||
#include "transfer-mgr.h"
|
||||
#include "sync-mgr.h"
|
||||
#include "wt-monitor.h"
|
||||
#include "mq-mgr.h"
|
||||
@ -24,10 +22,6 @@
|
||||
#include "http-tx-mgr.h"
|
||||
#include "filelock-mgr.h"
|
||||
|
||||
#include <searpc-client.h>
|
||||
|
||||
struct _CcnetClient;
|
||||
|
||||
|
||||
#define SEAFILE_TYPE_SESSION (seafile_session_get_type ())
|
||||
#define SEAFILE_SESSION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SEAFILE_TYPE_SESSION, SeafileSession))
|
||||
@ -40,36 +34,37 @@ struct _CcnetClient;
|
||||
typedef struct _SeafileSession SeafileSession;
|
||||
typedef struct _SeafileSessionClass SeafileSessionClass;
|
||||
|
||||
struct event_base;
|
||||
|
||||
struct _SeafileSession {
|
||||
GObject parent_instance;
|
||||
|
||||
struct _CcnetClient *session;
|
||||
struct event_base *ev_base;
|
||||
|
||||
char *client_id;
|
||||
char *client_name;
|
||||
|
||||
SearpcClient *ccnetrpc_client;
|
||||
SearpcClient *appletrpc_client;
|
||||
|
||||
char *seaf_dir;
|
||||
char *tmp_file_dir;
|
||||
char *worktree_dir; /* the default directory for
|
||||
* storing worktrees */
|
||||
char *ccnet_dir;
|
||||
sqlite3 *config_db;
|
||||
char *deleted_store;
|
||||
char *rpc_socket_path;
|
||||
|
||||
SeafBlockManager *block_mgr;
|
||||
SeafFSManager *fs_mgr;
|
||||
SeafCommitManager *commit_mgr;
|
||||
SeafBranchManager *branch_mgr;
|
||||
SeafRepoManager *repo_mgr;
|
||||
SeafTransferManager *transfer_mgr;
|
||||
SeafCloneManager *clone_mgr;
|
||||
SeafSyncManager *sync_mgr;
|
||||
SeafWTMonitor *wt_monitor;
|
||||
SeafMqManager *mq_mgr;
|
||||
|
||||
CEventManager *ev_mgr;
|
||||
CcnetJobManager *job_mgr;
|
||||
SeafJobManager *job_mgr;
|
||||
|
||||
HttpTxManager *http_tx_mgr;
|
||||
|
||||
@ -101,7 +96,7 @@ extern SeafileSession *seaf;
|
||||
SeafileSession *
|
||||
seafile_session_new(const char *seafile_dir,
|
||||
const char *worktree_dir,
|
||||
struct _CcnetClient *ccnet_session);
|
||||
const char *config_dir);
|
||||
void
|
||||
seafile_session_prepare (SeafileSession *session);
|
||||
|
||||
|
378
daemon/status.c
378
daemon/status.c
@ -1,378 +0,0 @@
|
||||
#include "common.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <glib.h>
|
||||
#include <glib/gstdio.h>
|
||||
|
||||
#include "seafile-session.h"
|
||||
#include "status.h"
|
||||
#include "fs-mgr.h"
|
||||
#include "index/index.h"
|
||||
#include "diff-simple.h"
|
||||
#include "vc-utils.h"
|
||||
#include "utils.h"
|
||||
#include "cdc/cdc.h"
|
||||
#include "log.h"
|
||||
|
||||
struct dir_entry {
|
||||
unsigned int len;
|
||||
char name[0]; /* more */
|
||||
};
|
||||
|
||||
struct dir_struct {
|
||||
int nr, alloc;
|
||||
int ignored_nr, ignored_alloc;
|
||||
enum {
|
||||
DIR_SHOW_IGNORED = 1<<0,
|
||||
DIR_SHOW_OTHER_DIRECTORIES = 1<<1,
|
||||
DIR_HIDE_EMPTY_DIRECTORIES = 1<<2,
|
||||
DIR_NO_GITLINKS = 1<<3,
|
||||
DIR_COLLECT_IGNORED = 1<<4
|
||||
} flags;
|
||||
struct dir_entry **entries;
|
||||
struct dir_entry **ignored;
|
||||
};
|
||||
|
||||
static struct dir_entry *
|
||||
dir_entry_new(const char *pathname, int len)
|
||||
{
|
||||
struct dir_entry *ent;
|
||||
|
||||
ent = malloc(sizeof(*ent) + len + 1);
|
||||
ent->len = len;
|
||||
memcpy(ent->name, pathname, len);
|
||||
ent->name[len] = 0;
|
||||
return ent;
|
||||
}
|
||||
|
||||
static struct dir_entry *
|
||||
dir_add_name(struct dir_struct *dir, const char *pathname,
|
||||
int len, struct index_state *index)
|
||||
{
|
||||
if (index_name_exists(index, pathname, len, 0))
|
||||
return NULL;
|
||||
|
||||
ALLOC_GROW(dir->entries, dir->nr+1, dir->alloc);
|
||||
return dir->entries[dir->nr++] = dir_entry_new(pathname, len);
|
||||
}
|
||||
|
||||
static inline int
|
||||
is_dot_or_dotdot(const char *name)
|
||||
{
|
||||
return (name[0] == '.' &&
|
||||
(name[1] == '\0' ||
|
||||
(name[1] == '.' && name[2] == '\0')));
|
||||
}
|
||||
|
||||
static int
|
||||
get_dtype(const char *dname, const char *path)
|
||||
{
|
||||
SeafStat st;
|
||||
int dtype = DT_UNKNOWN;
|
||||
char *realpath = g_build_path (PATH_SEPERATOR, path, dname, NULL);
|
||||
|
||||
if (!seaf_stat(realpath, &st)) {
|
||||
if (S_ISREG(st.st_mode))
|
||||
dtype = DT_REG;
|
||||
if (S_ISDIR(st.st_mode))
|
||||
dtype = DT_DIR;
|
||||
}
|
||||
|
||||
g_free(realpath);
|
||||
return dtype;
|
||||
}
|
||||
|
||||
static int
|
||||
read_directory_recursive(struct dir_struct *dir,
|
||||
const char *base, int baselen,
|
||||
int check_only,
|
||||
struct index_state *index,
|
||||
const char *worktree,
|
||||
IgnoreFunc ignore_func,
|
||||
void *data)
|
||||
{
|
||||
char *realpath = g_build_path (PATH_SEPERATOR, worktree, base, NULL);
|
||||
GDir *fdir = g_dir_open (realpath, 0, NULL);
|
||||
const char *dname;
|
||||
char *nfc_dname;
|
||||
int contents = 0;
|
||||
int dtype;
|
||||
|
||||
if (fdir) {
|
||||
char path[SEAF_PATH_MAX + 1];
|
||||
memcpy(path, base, baselen);
|
||||
while ((dname = g_dir_read_name(fdir)) != NULL) {
|
||||
int len = 0;
|
||||
|
||||
#ifdef __APPLE__
|
||||
nfc_dname = g_utf8_normalize (dname, -1, G_NORMALIZE_NFC);
|
||||
#else
|
||||
nfc_dname = g_strdup(dname);
|
||||
#endif
|
||||
|
||||
if (is_dot_or_dotdot(nfc_dname)) {
|
||||
g_free (nfc_dname);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ignore_func (realpath, nfc_dname, data)) {
|
||||
g_free (nfc_dname);
|
||||
continue;
|
||||
}
|
||||
|
||||
dtype = get_dtype(nfc_dname, realpath);
|
||||
switch (dtype) {
|
||||
case DT_REG:
|
||||
len = strlen(nfc_dname);
|
||||
memcpy(path + baselen, nfc_dname, len + 1);
|
||||
len = strlen(path);
|
||||
break;
|
||||
case DT_DIR:
|
||||
len = strlen(nfc_dname);
|
||||
memcpy(path + baselen, nfc_dname, len + 1);
|
||||
memcpy(path + baselen + len, "/", 2);
|
||||
len = strlen(path);
|
||||
read_directory_recursive(dir, path, len, 0,
|
||||
index, worktree, ignore_func, data);
|
||||
g_free (nfc_dname);
|
||||
continue;
|
||||
default: /* DT_UNKNOWN */
|
||||
len = 0;
|
||||
break;
|
||||
}
|
||||
if(len > 0)
|
||||
dir_add_name(dir, path, len, index);
|
||||
g_free (nfc_dname);
|
||||
}
|
||||
g_dir_close(fdir);
|
||||
}
|
||||
|
||||
g_free(realpath);
|
||||
return contents;
|
||||
}
|
||||
|
||||
static int
|
||||
cmp_name(const void *p1, const void *p2)
|
||||
{
|
||||
const struct dir_entry *e1 = *(const struct dir_entry **)p1;
|
||||
const struct dir_entry *e2 = *(const struct dir_entry **)p2;
|
||||
|
||||
return cache_name_compare(e1->name, e1->len,
|
||||
e2->name, e2->len);
|
||||
}
|
||||
|
||||
static int
|
||||
read_directory(struct dir_struct *dir,
|
||||
const char *worktree,
|
||||
struct index_state *index,
|
||||
IgnoreFunc ignore_func)
|
||||
{
|
||||
GList *ignore_list = NULL;
|
||||
|
||||
ignore_list = seaf_repo_load_ignore_files(worktree);
|
||||
read_directory_recursive(dir, "", 0, 0, index, worktree,
|
||||
ignore_func, ignore_list);
|
||||
seaf_repo_free_ignore_files(ignore_list);
|
||||
qsort(dir->entries, dir->nr, sizeof(struct dir_entry *), cmp_name);
|
||||
return dir->nr;
|
||||
}
|
||||
|
||||
void wt_status_collect_untracked(struct index_state *index,
|
||||
GList **results,
|
||||
const char *worktree,
|
||||
IgnoreFunc ignore_func)
|
||||
{
|
||||
int i;
|
||||
struct dir_struct dir;
|
||||
DiffEntry *de;
|
||||
|
||||
memset(&dir, 0, sizeof(dir));
|
||||
|
||||
read_directory(&dir, worktree, index, ignore_func);
|
||||
for (i = 0; i < dir.nr; i++) {
|
||||
struct dir_entry *ent = dir.entries[i];
|
||||
unsigned char sha1[20] = { 0 };
|
||||
|
||||
de = diff_entry_new (DIFF_TYPE_WORKTREE, DIFF_STATUS_ADDED, sha1, ent->name);
|
||||
*results = g_list_prepend (*results, de);
|
||||
|
||||
free(ent);
|
||||
}
|
||||
|
||||
free(dir.entries);
|
||||
}
|
||||
|
||||
void wt_status_collect_changes_worktree(struct index_state *index,
|
||||
GList **results,
|
||||
const char *worktree)
|
||||
{
|
||||
DiffEntry *de;
|
||||
int entries, i;
|
||||
|
||||
GList *ignore_list = seaf_repo_load_ignore_files (worktree);
|
||||
|
||||
entries = index->cache_nr;
|
||||
for (i = 0; i < entries; i++) {
|
||||
char *realpath;
|
||||
SeafStat st;
|
||||
struct cache_entry *ce = index->cache[i];
|
||||
int changed = 0;
|
||||
|
||||
if (ce_stage(ce)) {
|
||||
int mask = 0;
|
||||
|
||||
mask |= 1 << ce_stage(ce);
|
||||
while (i < entries) {
|
||||
struct cache_entry *nce = index->cache[i];
|
||||
|
||||
if (strcmp(ce->name, nce->name))
|
||||
break;
|
||||
|
||||
mask |= 1 << ce_stage(nce);
|
||||
i++;
|
||||
}
|
||||
|
||||
/*
|
||||
* Compensate for loop update
|
||||
*/
|
||||
i--;
|
||||
|
||||
de = diff_entry_new (DIFF_TYPE_WORKTREE, DIFF_STATUS_UNMERGED,
|
||||
ce->sha1, ce->name);
|
||||
de->unmerge_state = diff_unmerged_state (mask);
|
||||
*results = g_list_prepend (*results, de);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ce_uptodate(ce) || ce_skip_worktree(ce))
|
||||
continue;
|
||||
|
||||
realpath = g_build_path (PATH_SEPERATOR, worktree, ce->name, NULL);
|
||||
if (seaf_stat(realpath, &st) < 0) {
|
||||
if (errno != ENOENT && errno != ENOTDIR)
|
||||
changed = -1;
|
||||
else
|
||||
changed = 1;
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
if (changed < 0) {
|
||||
seaf_warning ("Faile to stat %s: %s\n", ce->name, strerror(errno));
|
||||
g_free (realpath);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ce->ce_ctime.sec == 0) {
|
||||
g_free (realpath);
|
||||
continue;
|
||||
}
|
||||
|
||||
de = diff_entry_new (DIFF_TYPE_WORKTREE, DIFF_STATUS_DELETED,
|
||||
ce->sha1, ce->name);
|
||||
*results = g_list_prepend (*results, de);
|
||||
g_free (realpath);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (S_ISDIR (ce->ce_mode)) {
|
||||
g_free (realpath);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Don't check changes to ignored files.
|
||||
* This can happen when a file is committed and then added to
|
||||
* ignore.txt. After that changes to this file will not committed,
|
||||
* and it should be ignored here.
|
||||
*/
|
||||
if (seaf_repo_check_ignore_file (ignore_list, realpath)) {
|
||||
g_free (realpath);
|
||||
continue;
|
||||
}
|
||||
|
||||
g_free (realpath);
|
||||
|
||||
changed = ie_match_stat (ce, &st, 0);
|
||||
if (!changed) {
|
||||
ce_mark_uptodate (ce);
|
||||
continue;
|
||||
}
|
||||
|
||||
de = diff_entry_new (DIFF_TYPE_WORKTREE, DIFF_STATUS_MODIFIED,
|
||||
ce->sha1, ce->name);
|
||||
*results = g_list_prepend (*results, de);
|
||||
}
|
||||
|
||||
seaf_repo_free_ignore_files (ignore_list);
|
||||
}
|
||||
|
||||
static struct cache_entry *
|
||||
next_cache_entry(struct index_state *index, int *pos)
|
||||
{
|
||||
while (*pos < index->cache_nr) {
|
||||
struct cache_entry *ce = index->cache[*pos];
|
||||
(*pos)++;
|
||||
if (!(ce->ce_flags & CE_UNPACKED))
|
||||
return ce;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
wt_status_collect_changes_index (struct index_state *index,
|
||||
GList **results,
|
||||
SeafRepo *repo)
|
||||
{
|
||||
SeafFSManager *fs_mgr;
|
||||
SeafCommit *head;
|
||||
int pos = 0;
|
||||
DiffEntry *de;
|
||||
|
||||
fs_mgr = repo->manager->seaf->fs_mgr;
|
||||
head = seaf_commit_manager_get_commit (seaf->commit_mgr,
|
||||
repo->id, repo->version,
|
||||
repo->head->commit_id);
|
||||
if (!head) {
|
||||
seaf_warning ("Failed to get commit %s:%s.\n",
|
||||
repo->id, repo->head->commit_id);
|
||||
return;
|
||||
}
|
||||
|
||||
mark_all_ce_unused (index);
|
||||
|
||||
/* if repo is initial, we don't need to check index changes */
|
||||
if (strncmp(EMPTY_SHA1, head->root_id, 40) != 0) {
|
||||
SeafDir *root;
|
||||
|
||||
/* call diff_index to get status */
|
||||
root = seaf_fs_manager_get_seafdir (fs_mgr,
|
||||
repo->id,
|
||||
repo->version,
|
||||
head->root_id);
|
||||
if (!root) {
|
||||
seaf_warning ("Failed to get root %s:%s.\n",
|
||||
repo->id, head->root_id);
|
||||
seaf_commit_unref (head);
|
||||
return;
|
||||
}
|
||||
|
||||
if (diff_index(repo->id, repo->version, index, root, results) < 0)
|
||||
seaf_warning("diff index failed\n");
|
||||
seaf_dir_free (root);
|
||||
seaf_commit_unref (head);
|
||||
return;
|
||||
}
|
||||
seaf_commit_unref (head);
|
||||
|
||||
while (1) {
|
||||
struct cache_entry *ce = next_cache_entry(index, &pos);
|
||||
|
||||
if (!ce || ce_stage(ce))
|
||||
break;
|
||||
|
||||
ce->ce_flags |= CE_UNPACKED;
|
||||
de = diff_entry_new (DIFF_TYPE_INDEX, DIFF_STATUS_ADDED, ce->sha1, ce->name);
|
||||
*results = g_list_prepend (*results, de);
|
||||
}
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
#ifndef STATUS_H
|
||||
#define STATUS_H
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#include "repo-mgr.h"
|
||||
#include "index/index.h"
|
||||
#include "diff-simple.h"
|
||||
|
||||
typedef gboolean (*IgnoreFunc) (const char *basepath, const char *filename, void *data);
|
||||
|
||||
void
|
||||
wt_status_collect_changes_worktree(struct index_state *index,
|
||||
GList **results,
|
||||
const char *worktree);
|
||||
|
||||
void
|
||||
wt_status_collect_untracked(struct index_state *index,
|
||||
GList **results,
|
||||
const char *worktree,
|
||||
IgnoreFunc ignore_func);
|
||||
|
||||
void
|
||||
wt_status_collect_changes_index (struct index_state *index,
|
||||
GList **results,
|
||||
SeafRepo *repo);
|
||||
|
||||
#endif /* STATUS_H */
|
1195
daemon/sync-mgr.c
1195
daemon/sync-mgr.c
File diff suppressed because it is too large
Load Diff
@ -9,7 +9,7 @@ typedef struct _SyncTask SyncTask;
|
||||
typedef struct _SeafSyncManager SeafSyncManager;
|
||||
typedef struct _SeafSyncManagerPriv SeafSyncManagerPriv;
|
||||
|
||||
struct CcnetTimer;
|
||||
struct SeafTimer;
|
||||
|
||||
struct _SyncInfo {
|
||||
char repo_id[41]; /* the repo */
|
||||
@ -86,12 +86,10 @@ struct _SyncTask {
|
||||
char *err_detail;
|
||||
char *tx_id;
|
||||
char *token;
|
||||
struct CcnetTimer *commit_timer;
|
||||
struct SeafTimer *commit_timer;
|
||||
|
||||
gboolean server_side_merge;
|
||||
gboolean uploaded;
|
||||
|
||||
gboolean http_sync;
|
||||
int http_version;
|
||||
|
||||
SeafRepo *repo; /* for convenience, only valid when in_sync. */
|
||||
@ -121,7 +119,6 @@ struct _SeafSyncManager {
|
||||
gboolean commit_job_running;
|
||||
int sync_interval;
|
||||
|
||||
GHashTable *server_states;
|
||||
GHashTable *http_server_states;
|
||||
|
||||
/* Sent/recv bytes from all transfer tasks in this second.
|
||||
|
89
daemon/timer.c
Normal file
89
daemon/timer.c
Normal file
@ -0,0 +1,89 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
|
||||
#include <event2/event.h>
|
||||
#include <event2/event_compat.h>
|
||||
#include <event2/event_struct.h>
|
||||
#else
|
||||
#include <event.h>
|
||||
#endif
|
||||
|
||||
#include <sys/time.h>
|
||||
|
||||
#include "seafile-session.h"
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
#include "timer.h"
|
||||
|
||||
struct SeafTimer
|
||||
{
|
||||
struct event *event;
|
||||
struct timeval tv;
|
||||
TimerCB func;
|
||||
void *user_data;
|
||||
uint8_t in_callback;
|
||||
};
|
||||
|
||||
static void
|
||||
timer_callback (evutil_socket_t fd, short event, void *vtimer)
|
||||
{
|
||||
int more;
|
||||
struct SeafTimer *timer = vtimer;
|
||||
|
||||
timer->in_callback = 1;
|
||||
more = (*timer->func) (timer->user_data);
|
||||
timer->in_callback = 0;
|
||||
|
||||
if (more)
|
||||
evtimer_add (timer->event, &timer->tv);
|
||||
else
|
||||
seaf_timer_free (&timer);
|
||||
}
|
||||
|
||||
void
|
||||
seaf_timer_free (SeafTimer **ptimer)
|
||||
{
|
||||
SeafTimer *timer;
|
||||
|
||||
/* zero out the argument passed in */
|
||||
g_return_if_fail (ptimer);
|
||||
|
||||
timer = *ptimer;
|
||||
*ptimer = NULL;
|
||||
|
||||
/* destroy the timer directly or via the command queue */
|
||||
if (timer && !timer->in_callback)
|
||||
{
|
||||
event_del (timer->event);
|
||||
event_free (timer->event);
|
||||
g_free (timer);
|
||||
}
|
||||
}
|
||||
|
||||
struct timeval
|
||||
timeval_from_msec (uint64_t milliseconds)
|
||||
{
|
||||
struct timeval ret;
|
||||
const uint64_t microseconds = milliseconds * 1000;
|
||||
ret.tv_sec = microseconds / 1000000;
|
||||
ret.tv_usec = microseconds % 1000000;
|
||||
return ret;
|
||||
}
|
||||
|
||||
SeafTimer*
|
||||
seaf_timer_new (TimerCB func,
|
||||
void *user_data,
|
||||
uint64_t interval_milliseconds)
|
||||
{
|
||||
SeafTimer *timer = g_new0 (SeafTimer, 1);
|
||||
|
||||
timer->tv = timeval_from_msec (interval_milliseconds);
|
||||
timer->func = func;
|
||||
timer->user_data = user_data;
|
||||
|
||||
timer->event = evtimer_new (seaf->ev_base, timer_callback, timer);
|
||||
evtimer_add (timer->event, &timer->tv);
|
||||
|
||||
return timer;
|
||||
}
|
28
daemon/timer.h
Normal file
28
daemon/timer.h
Normal file
@ -0,0 +1,28 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#ifndef SEAF_TIMER_H
|
||||
#define SEAF_TIMER_H
|
||||
|
||||
/* return TRUE to reschedule the timer, return FALSE to cancle the timer */
|
||||
typedef int (*TimerCB) (void *data);
|
||||
|
||||
struct SeafTimer;
|
||||
|
||||
typedef struct SeafTimer SeafTimer;
|
||||
|
||||
/**
|
||||
* Calls timer_func(user_data) after the specified interval.
|
||||
* The timer is freed if timer_func returns zero.
|
||||
* Otherwise, it's called again after the same interval.
|
||||
*/
|
||||
SeafTimer* seaf_timer_new (TimerCB func,
|
||||
void *user_data,
|
||||
uint64_t timeout_milliseconds);
|
||||
|
||||
/**
|
||||
* Frees a timer and sets the timer pointer to NULL.
|
||||
*/
|
||||
void seaf_timer_free (SeafTimer **timer);
|
||||
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
@ -1,285 +0,0 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#ifndef TRANSFER_MGR_H
|
||||
#define TRANSFER_MGR_H
|
||||
|
||||
#include <glib.h>
|
||||
#include <ccnet/timer.h>
|
||||
#include <ccnet/peer.h>
|
||||
|
||||
#include "object-list.h"
|
||||
#include "repo-mgr.h"
|
||||
#include "fs-mgr.h"
|
||||
#include "branch-mgr.h"
|
||||
|
||||
/*
|
||||
* Transfer Task.
|
||||
*/
|
||||
|
||||
enum {
|
||||
TASK_TYPE_DOWNLOAD = 0,
|
||||
TASK_TYPE_UPLOAD,
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* The state that can be set by user.
|
||||
*
|
||||
* A task in NORMAL state can be canceled;
|
||||
* A task in RT_STATE_FINISHED can be removed.
|
||||
*/
|
||||
enum TaskState {
|
||||
TASK_STATE_NORMAL = 0,
|
||||
TASK_STATE_CANCELED,
|
||||
TASK_STATE_FINISHED,
|
||||
TASK_STATE_ERROR,
|
||||
N_TASK_STATE,
|
||||
};
|
||||
|
||||
enum TaskRuntimeState {
|
||||
TASK_RT_STATE_INIT = 0,
|
||||
TASK_RT_STATE_CHECK,
|
||||
TASK_RT_STATE_COMMIT,
|
||||
TASK_RT_STATE_FS,
|
||||
TASK_RT_STATE_CHECK_BLOCKS,
|
||||
TASK_RT_STATE_CHUNK_SERVER,
|
||||
TASK_RT_STATE_DATA,
|
||||
TASK_RT_STATE_UPDATE_BRANCH, /* Only used in upload. */
|
||||
TASK_RT_STATE_FINISHED,
|
||||
TASK_RT_STATE_NETDOWN,
|
||||
N_TASK_RT_STATE,
|
||||
};
|
||||
|
||||
enum TaskError {
|
||||
TASK_OK = 0,
|
||||
TASK_ERR_UNKNOWN,
|
||||
TASK_ERR_NO_SERVICE,
|
||||
TASK_ERR_PROC_PERM_ERR,
|
||||
TASK_ERR_CHECK_UPLOAD_START,
|
||||
TASK_ERR_CHECK_DOWNLOAD_START,
|
||||
TASK_ERR_ACCESS_DENIED,
|
||||
TASK_ERR_BAD_REPO_ID,
|
||||
TASK_ERR_UPLOAD_COMMIT_START,
|
||||
TASK_ERR_DOWNLOAD_COMMIT_START,
|
||||
TASK_ERR_UPLOAD_COMMIT,
|
||||
TASK_ERR_DOWNLOAD_COMMIT,
|
||||
TASK_ERR_UPLOAD_FS_START,
|
||||
TASK_ERR_DOWNLOAD_FS_START,
|
||||
TASK_ERR_LOAD_FS,
|
||||
TASK_ERR_UPLOAD_FS,
|
||||
TASK_ERR_DOWNLOAD_FS,
|
||||
TASK_ERR_LOAD_BLOCK_LIST,
|
||||
TASK_ERR_START_UPDATE_BRANCH,
|
||||
TASK_ERR_NOT_FAST_FORWARD,
|
||||
TASK_ERR_QUOTA_FULL,
|
||||
TASK_ERR_CHECK_QUOTA,
|
||||
TASK_ERR_PROTOCOL_VERSION,
|
||||
TASK_ERR_BAD_LOCAL_BRANCH,
|
||||
TASK_ERR_CHECK_BLOCK_LIST,
|
||||
TASK_ERR_GET_CHUNK_SERVER,
|
||||
TASK_ERR_START_BLOCK_CLIENT,
|
||||
TASK_ERR_UPLOAD_BLOCKS,
|
||||
TASK_ERR_DOWNLOAD_BLOCKS,
|
||||
TASK_ERR_DEPRECATED_SERVER,
|
||||
TASK_ERR_FILES_LOCKED,
|
||||
N_TASK_ERROR,
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
char *addr;
|
||||
int port;
|
||||
} ChunkServer;
|
||||
|
||||
enum {
|
||||
BLOCK_CLIENT_UNKNOWN = 0,
|
||||
BLOCK_CLIENT_SUCCESS,
|
||||
BLOCK_CLIENT_FAILED,
|
||||
BLOCK_CLIENT_NET_ERROR,
|
||||
BLOCK_CLIENT_SERVER_ERROR,
|
||||
BLOCK_CLIENT_CANCELED,
|
||||
|
||||
/* result codes only used in interactive mode. */
|
||||
BLOCK_CLIENT_READY,
|
||||
BLOCK_CLIENT_ENDED,
|
||||
};
|
||||
|
||||
#define BLOCK_TX_SESSION_KEY_LEN 32
|
||||
|
||||
struct _TransferTask;
|
||||
|
||||
typedef struct _BlockTxInfo {
|
||||
struct _TransferTask *task;
|
||||
ChunkServer *cs;
|
||||
unsigned char session_key[BLOCK_TX_SESSION_KEY_LEN];
|
||||
unsigned char *enc_session_key; /* encrypted session_key */
|
||||
int enc_key_len;
|
||||
int cmd_pipe[2]; /* used to notify cancel */
|
||||
int done_pipe[2]; /* notify block transfer done */
|
||||
int result;
|
||||
int n_failure;
|
||||
/* TRUE if the client only transfer one batch of blocks and end.*/
|
||||
gboolean transfer_once;
|
||||
gint ready_for_transfer;
|
||||
} BlockTxInfo;
|
||||
|
||||
struct _SeafTransferManager;
|
||||
|
||||
struct _TransferTask {
|
||||
struct _SeafTransferManager *manager;
|
||||
char tx_id[37];
|
||||
char repo_id[37];
|
||||
int repo_version;
|
||||
char *token;
|
||||
char *session_token;
|
||||
int protocol_version;
|
||||
char *from_branch;
|
||||
char *to_branch;
|
||||
char head[41];
|
||||
char remote_head[41];
|
||||
int state; /* NORMAL, STOPPED, CANCELED */
|
||||
int runtime_state;
|
||||
int last_runtime_state;
|
||||
int type;
|
||||
gboolean is_clone; /* TRUE when fetching a new repo. */
|
||||
char *email;
|
||||
int error;
|
||||
|
||||
char *dest_id;
|
||||
|
||||
ObjectList *commits; /* commits need to be uploaded */
|
||||
ObjectList *fs_roots; /* the root of file systems to be sent/get */
|
||||
|
||||
GList *chunk_servers;
|
||||
BlockList *block_list;
|
||||
gint tx_bytes; /* bytes transferred in the this second. */
|
||||
gint last_tx_bytes; /* bytes transferred in the last second. */
|
||||
|
||||
/* Fields only used by upload task. */
|
||||
int n_uploaded;
|
||||
|
||||
/* For new block transfer protocol */
|
||||
BlockTxInfo *tx_info;
|
||||
GQueue *block_ids;
|
||||
|
||||
gboolean server_side_merge;
|
||||
/* These two fields are only used for new syncing protocol. */
|
||||
char *passwd;
|
||||
char *worktree;
|
||||
|
||||
/* Used to display download progress for new syncing protocol */
|
||||
int n_to_download;
|
||||
int n_downloaded;
|
||||
|
||||
gint64 rsize; /* size remain */
|
||||
gint64 dsize; /* size done */
|
||||
};
|
||||
typedef struct _TransferTask TransferTask;
|
||||
|
||||
const char *
|
||||
task_state_to_str (int state);
|
||||
|
||||
const char *
|
||||
task_rt_state_to_str (int rt_state);
|
||||
|
||||
const char *
|
||||
task_error_str (int task_errno);
|
||||
|
||||
int
|
||||
transfer_task_get_rate (TransferTask *task);
|
||||
|
||||
int
|
||||
transfer_task_get_done_blocks (TransferTask *task);
|
||||
|
||||
void
|
||||
transfer_task_set_error (TransferTask *task, int error);
|
||||
|
||||
void
|
||||
transfer_task_set_netdown (TransferTask *task);
|
||||
|
||||
void
|
||||
transition_state_to_error (TransferTask *task, int task_errno);
|
||||
|
||||
/*
|
||||
* Transfer Manager
|
||||
*/
|
||||
|
||||
struct _SeafileSession;
|
||||
|
||||
struct _SeafTransferManager {
|
||||
struct _SeafileSession *seaf;
|
||||
sqlite3 *db;
|
||||
|
||||
GHashTable *download_tasks;
|
||||
GHashTable *upload_tasks;
|
||||
|
||||
CcnetTimer *schedule_timer;
|
||||
};
|
||||
|
||||
typedef struct _SeafTransferManager SeafTransferManager;
|
||||
|
||||
SeafTransferManager *seaf_transfer_manager_new (struct _SeafileSession *seaf);
|
||||
|
||||
int seaf_transfer_manager_start (SeafTransferManager *manager);
|
||||
|
||||
char *
|
||||
seaf_transfer_manager_add_download (SeafTransferManager *manager,
|
||||
const char *repo_id,
|
||||
int repo_version,
|
||||
const char *peer_id,
|
||||
const char *from_branch,
|
||||
const char *to_branch,
|
||||
const char *token,
|
||||
gboolean server_side_merge,
|
||||
const char *passwd,
|
||||
const char *worktree,
|
||||
const char *email,
|
||||
GError **error);
|
||||
|
||||
char *
|
||||
seaf_transfer_manager_add_upload (SeafTransferManager *manager,
|
||||
const char *repo_id,
|
||||
int repo_version,
|
||||
const char *peer_id,
|
||||
const char *from_branch,
|
||||
const char *to_branch,
|
||||
const char *token,
|
||||
gboolean server_side_merge,
|
||||
GError **error);
|
||||
|
||||
GList*
|
||||
seaf_transfer_manager_get_upload_tasks (SeafTransferManager *manager);
|
||||
|
||||
GList*
|
||||
seaf_transfer_manager_get_download_tasks (SeafTransferManager *manager);
|
||||
|
||||
/* find running tranfer of a repo */
|
||||
TransferTask*
|
||||
seaf_transfer_manager_find_transfer_by_repo (SeafTransferManager *manager,
|
||||
const char *repo_id);
|
||||
|
||||
void
|
||||
seaf_transfer_manager_remove_task (SeafTransferManager *manager,
|
||||
const char *tx_id,
|
||||
int task_type);
|
||||
|
||||
void
|
||||
seaf_transfer_manager_cancel_task (SeafTransferManager *manager,
|
||||
const char *tx_id,
|
||||
int task_type);
|
||||
|
||||
GList *
|
||||
seaf_transfer_manager_get_clone_heads (SeafTransferManager *mgr);
|
||||
|
||||
char *
|
||||
seaf_transfer_manager_get_clone_head (SeafTransferManager *mgr,
|
||||
const char *repo_id);
|
||||
|
||||
/*
|
||||
* return the status code of block tx client.
|
||||
*/
|
||||
int
|
||||
seaf_transfer_manager_download_file_blocks (SeafTransferManager *manager,
|
||||
TransferTask *task,
|
||||
const char *file_id);
|
||||
|
||||
#endif
|
@ -13,7 +13,6 @@
|
||||
|
||||
#include "utils.h"
|
||||
#include "fs-mgr.h"
|
||||
#include "merge.h"
|
||||
#include "vc-utils.h"
|
||||
#include "vc-common.h"
|
||||
#include "index/index.h"
|
||||
@ -281,62 +280,6 @@ out:
|
||||
|
||||
#endif /* WIN32 */
|
||||
|
||||
static int
|
||||
unlink_entry (struct cache_entry *ce, struct unpack_trees_options *o)
|
||||
{
|
||||
char path[SEAF_PATH_MAX];
|
||||
SeafStat st;
|
||||
int base_len = strlen(o->base);
|
||||
int len = ce_namelen(ce);
|
||||
int offset;
|
||||
|
||||
if (!len) {
|
||||
seaf_warning ("entry name should not be empty.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
snprintf (path, SEAF_PATH_MAX, "%s/%s", o->base, ce->name);
|
||||
|
||||
if (!S_ISDIR(ce->ce_mode)) {
|
||||
/* file doesn't exist in work tree */
|
||||
if (seaf_stat (path, &st) < 0 || !S_ISREG(st.st_mode)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* file has been changed. */
|
||||
if (!o->reset &&
|
||||
(ce->current_mtime != st.st_mtime)) {
|
||||
seaf_warning ("File %s is changed. Skip removing the file.\n", path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* first unlink the file. */
|
||||
if (seaf_util_unlink (path) < 0) {
|
||||
seaf_warning ("Failed to remove %s: %s.\n", path, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
if (seaf_remove_empty_dir (path) < 0) {
|
||||
seaf_warning ("Failed to remove dir %s: %s.\n", path, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* then remove all empty directories upwards. */
|
||||
offset = base_len + len;
|
||||
do {
|
||||
if (path[offset] == '/') {
|
||||
path[offset] = '\0';
|
||||
int ret = seaf_remove_empty_dir (path);
|
||||
if (ret < 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while (--offset > base_len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
compute_file_id_with_cdc (const char *path, SeafStat *st,
|
||||
SeafileCrypt *crypt, int repo_version,
|
||||
@ -399,310 +342,6 @@ compare_file_content (const char *path, SeafStat *st, const unsigned char *ce_sh
|
||||
}
|
||||
}
|
||||
|
||||
#if defined WIN32 || defined __APPLE__
|
||||
|
||||
/*
|
||||
* If the names are different case-sensitively but the same case-insensitively,
|
||||
* it's a case conflict.
|
||||
* Note that the names are in UTF-8, so we use UTF-8 function to compare them.
|
||||
*/
|
||||
static gboolean
|
||||
case_conflict_utf8 (const char *name1, const char *name2)
|
||||
{
|
||||
char *casefold1, *casefold2;
|
||||
gboolean ret;
|
||||
|
||||
if (strcmp (name1, name2) == 0)
|
||||
return FALSE;
|
||||
|
||||
casefold1 = g_utf8_casefold (name1, -1);
|
||||
casefold2 = g_utf8_casefold (name2, -1);
|
||||
ret = (g_utf8_collate (casefold1, casefold2) == 0);
|
||||
|
||||
g_free (casefold1);
|
||||
g_free (casefold2);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifndef WIN32
|
||||
|
||||
static gboolean
|
||||
case_conflict_exists (const char *dir_path, const char *new_dname,
|
||||
char **conflict_dname)
|
||||
{
|
||||
GDir *dir;
|
||||
const char *dname;
|
||||
gboolean is_case_conflict = FALSE;
|
||||
GError *error = NULL;
|
||||
|
||||
/* Don't generate "case conflict" files for a case-conflicted file. */
|
||||
if (strstr (new_dname, "case conflict") != NULL) {
|
||||
return is_case_conflict;
|
||||
}
|
||||
|
||||
dir = g_dir_open (dir_path, 0, &error);
|
||||
if (!dir && error) {
|
||||
seaf_warning ("Failed to open dir %s: %s.\n", dir_path, error->message);
|
||||
g_error_free (error);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
dname = g_dir_read_name (dir);
|
||||
if (!dname)
|
||||
break;
|
||||
|
||||
char *norm_dname = g_utf8_normalize (dname, -1, G_NORMALIZE_NFC);
|
||||
if (case_conflict_utf8 (norm_dname, new_dname)) {
|
||||
is_case_conflict = TRUE;
|
||||
*conflict_dname = norm_dname;
|
||||
break;
|
||||
}
|
||||
g_free (norm_dname);
|
||||
}
|
||||
g_dir_close (dir);
|
||||
|
||||
return is_case_conflict;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
typedef struct CaseConflictData {
|
||||
const char *new_dname;
|
||||
gboolean is_case_conflict;
|
||||
char *conflict_dname;
|
||||
} CaseConflictData;
|
||||
|
||||
static int
|
||||
check_case_conflict_cb (wchar_t *parent, WIN32_FIND_DATAW *fdata,
|
||||
void *user_data, gboolean *stop)
|
||||
{
|
||||
CaseConflictData *data = user_data;
|
||||
char *dname = NULL;
|
||||
|
||||
dname = g_utf16_to_utf8 (fdata->cFileName, -1, NULL, NULL, NULL);
|
||||
|
||||
if (case_conflict_utf8 (dname, data->new_dname)) {
|
||||
data->is_case_conflict = TRUE;
|
||||
data->conflict_dname = g_strdup(dname);
|
||||
*stop = TRUE;
|
||||
}
|
||||
|
||||
g_free (dname);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
case_conflict_exists (const char *dir_path, const char *new_dname,
|
||||
char **conflict_dname)
|
||||
{
|
||||
wchar_t *dir_path_w;
|
||||
gboolean is_case_conflict = FALSE;
|
||||
CaseConflictData data;
|
||||
|
||||
/* Don't generate "case conflict" files for a case-conflicted file. */
|
||||
if (strstr (new_dname, "case conflict") != NULL) {
|
||||
return is_case_conflict;
|
||||
}
|
||||
|
||||
dir_path_w = win32_long_path (dir_path);
|
||||
|
||||
memset (&data, 0, sizeof(data));
|
||||
data.new_dname = new_dname;
|
||||
|
||||
if (traverse_directory_win32 (dir_path_w, check_case_conflict_cb, &data) < 0)
|
||||
goto out;
|
||||
|
||||
is_case_conflict = data.is_case_conflict;
|
||||
*conflict_dname = data.conflict_dname;
|
||||
|
||||
out:
|
||||
g_free (dir_path_w);
|
||||
return is_case_conflict;
|
||||
}
|
||||
|
||||
#endif /* WIN32 */
|
||||
|
||||
/*
|
||||
* If files "test (case conflict 1).txt" and "Test (case conflict 2).txt" exist,
|
||||
* and we have to checkout "TEST.txt", it will be checked out to "TEST
|
||||
* (case conflict 3).txt".
|
||||
* To prevent generating too many case conflict files (in case of bug or other
|
||||
* reasons), no more than 10 case conflict files will be generated for the same
|
||||
* filename. After that, all later confilcted files will use the last conflict
|
||||
* file name.
|
||||
*/
|
||||
static char *
|
||||
gen_case_conflict_free_dname (const char *dir_path, const char *dname)
|
||||
{
|
||||
char *copy = g_strdup (dname);
|
||||
GString *buf = g_string_new (NULL);
|
||||
char ret_dname[256];
|
||||
char *dot, *ext;
|
||||
int cnt = 1;
|
||||
|
||||
dot = strrchr (copy, '.');
|
||||
|
||||
while (cnt < 11) {
|
||||
if (dot != NULL) {
|
||||
*dot = '\0';
|
||||
ext = dot + 1;
|
||||
snprintf (ret_dname, sizeof(ret_dname), "%s (case conflict %d).%s",
|
||||
copy, cnt, ext);
|
||||
g_string_printf (buf, "%s/%s (case conflict %d).%s",
|
||||
dir_path, copy, cnt, ext);
|
||||
} else {
|
||||
snprintf (ret_dname, sizeof(ret_dname), "%s (case conflict %d)",
|
||||
copy, cnt);
|
||||
g_string_printf (buf, "%s/%s (case conflict %d)",
|
||||
dir_path, copy, cnt);
|
||||
}
|
||||
|
||||
if (!seaf_util_exists (buf->str))
|
||||
break;
|
||||
|
||||
g_string_truncate (buf, 0);
|
||||
++cnt;
|
||||
}
|
||||
|
||||
g_free (copy);
|
||||
g_string_free (buf, TRUE);
|
||||
|
||||
return g_strdup(ret_dname);
|
||||
}
|
||||
|
||||
/*
|
||||
* @conflict_hash: conflicting_dir_path -> conflict_free_dname
|
||||
* @no_conflict_hash: a hash table to remember dirs that have no case conflict.
|
||||
*/
|
||||
char *
|
||||
build_case_conflict_free_path (const char *worktree,
|
||||
const char *ce_name,
|
||||
GHashTable *conflict_hash,
|
||||
GHashTable *no_conflict_hash,
|
||||
gboolean *is_case_conflict,
|
||||
gboolean is_rename)
|
||||
{
|
||||
GString *buf = g_string_new (worktree);
|
||||
char **components, *ptr;
|
||||
guint i, n_comps;
|
||||
static int dummy;
|
||||
char *conflict_dname = NULL;
|
||||
|
||||
components = g_strsplit (ce_name, "/", -1);
|
||||
n_comps = g_strv_length (components);
|
||||
for (i = 0; i < n_comps; ++i) {
|
||||
char *path = NULL, *dname = NULL;
|
||||
SeafStat st;
|
||||
|
||||
ptr = components[i];
|
||||
|
||||
path = g_build_path ("/", buf->str, ptr, NULL);
|
||||
/* If path doesn't exist, case conflict is not possible. */
|
||||
if (seaf_stat (path, &st) < 0) {
|
||||
if (i != n_comps - 1) {
|
||||
if (seaf_util_mkdir (path, 0777) < 0) {
|
||||
seaf_warning ("Failed to create dir %s.\n", path);
|
||||
g_free (path);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
g_string_append_printf (buf, "/%s", ptr);
|
||||
g_free (path);
|
||||
continue;
|
||||
}
|
||||
|
||||
dname = g_hash_table_lookup (conflict_hash, path);
|
||||
if (dname) {
|
||||
/* We've detected (and fixed) case conflict for this dir before. */
|
||||
*is_case_conflict = TRUE;
|
||||
g_free (path);
|
||||
g_string_append_printf (buf, "/%s", dname);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (g_hash_table_lookup (no_conflict_hash, path) != NULL) {
|
||||
/* We've confirmed this dir has no case conflict before. */
|
||||
g_free (path);
|
||||
g_string_append_printf (buf, "/%s", ptr);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* No luck in the hash tables, we have to run case conflict detection. */
|
||||
if (!case_conflict_exists (buf->str, ptr, &conflict_dname)) {
|
||||
/* No case conflict. */
|
||||
if (i != n_comps - 1)
|
||||
g_hash_table_insert (no_conflict_hash,
|
||||
g_strdup(path),
|
||||
&dummy);
|
||||
g_free (path);
|
||||
g_string_append_printf (buf, "/%s", ptr);
|
||||
continue;
|
||||
}
|
||||
|
||||
*is_case_conflict = TRUE;
|
||||
|
||||
/* If case conflict, create a conflict free path and
|
||||
* remember it in the hash table.
|
||||
*/
|
||||
|
||||
if (!is_rename) {
|
||||
dname = gen_case_conflict_free_dname (buf->str, ptr);
|
||||
|
||||
char *case_conflict_free_path = g_build_path ("/", buf->str, dname, NULL);
|
||||
if (i != n_comps - 1) {
|
||||
if (seaf_util_mkdir (case_conflict_free_path, 0777) < 0) {
|
||||
seaf_warning ("Failed to create dir %s.\n",
|
||||
case_conflict_free_path);
|
||||
g_free (path);
|
||||
g_free (dname);
|
||||
g_free (case_conflict_free_path);
|
||||
goto error;
|
||||
}
|
||||
|
||||
g_hash_table_insert (conflict_hash, g_strdup(path), g_strdup(dname));
|
||||
}
|
||||
|
||||
g_string_append_printf (buf, "/%s", dname);
|
||||
|
||||
g_free (dname);
|
||||
g_free (case_conflict_free_path);
|
||||
} else {
|
||||
char *src_path = g_build_path ("/", buf->str, conflict_dname, NULL);
|
||||
|
||||
if (i != (n_comps - 1) && seaf_util_rename (src_path, path) < 0) {
|
||||
seaf_warning ("Failed to rename %s to %s: %s.\n",
|
||||
src_path, path, strerror(errno));
|
||||
g_free (path);
|
||||
g_free (src_path);
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Since the exsiting dir in the worktree has been renamed,
|
||||
* there is no more case conflict.
|
||||
*/
|
||||
g_hash_table_insert (no_conflict_hash, g_strdup(path), &dummy);
|
||||
|
||||
g_string_append_printf (buf, "/%s", ptr);
|
||||
|
||||
g_free (src_path);
|
||||
}
|
||||
|
||||
g_free (conflict_dname);
|
||||
g_free (path);
|
||||
}
|
||||
|
||||
g_strfreev (components);
|
||||
return g_string_free (buf, FALSE);
|
||||
|
||||
error:
|
||||
g_strfreev (components);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif /* defined WIN32 || defined __APPLE__ */
|
||||
|
||||
char *
|
||||
build_checkout_path (const char *worktree, const char *ce_name, int len)
|
||||
{
|
||||
@ -744,170 +383,6 @@ build_checkout_path (const char *worktree, const char *ce_name, int len)
|
||||
return g_strdup(path);
|
||||
}
|
||||
|
||||
static int
|
||||
checkout_entry (struct cache_entry *ce,
|
||||
struct unpack_trees_options *o,
|
||||
gboolean recover_merge,
|
||||
const char *conflict_head_id,
|
||||
GHashTable *conflict_hash,
|
||||
GHashTable *no_conflict_hash)
|
||||
{
|
||||
char *path_in, *path;
|
||||
SeafStat st;
|
||||
char file_id[41];
|
||||
gboolean case_conflict = FALSE;
|
||||
gboolean force_conflict = FALSE;
|
||||
|
||||
path_in = g_build_path ("/", o->base, ce->name, NULL);
|
||||
#if defined WIN32 || defined __APPLE__
|
||||
path = build_case_conflict_free_path (o->base, ce->name,
|
||||
conflict_hash, no_conflict_hash,
|
||||
&case_conflict,
|
||||
FALSE);
|
||||
#else
|
||||
path = build_checkout_path (o->base, ce->name, ce_namelen(ce));
|
||||
#endif
|
||||
|
||||
g_free (path_in);
|
||||
if (!path)
|
||||
return -1;
|
||||
|
||||
if (!S_ISDIR(ce->ce_mode)) {
|
||||
/* In case that we're replacing an empty dir with a file,
|
||||
* we need first to remove the empty dir.
|
||||
*/
|
||||
if (seaf_stat (path, &st) == 0 && S_ISDIR(st.st_mode)) {
|
||||
if (seaf_util_rmdir (path) < 0) {
|
||||
seaf_warning ("Failed to remove dir %s: %s\n", path, strerror(errno));
|
||||
/* Don't quit since we can handle conflict later. */
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (seaf_util_mkdir (path, 0777) < 0) {
|
||||
seaf_warning ("Failed to create empty dir %s in checkout.\n", path);
|
||||
g_free (path);
|
||||
return -1;
|
||||
}
|
||||
if (ce->ce_mtime.sec != 0 &&
|
||||
seaf_set_file_time (path, ce->ce_mtime.sec) < 0) {
|
||||
seaf_warning ("Failed to set mtime for %s.\n", path);
|
||||
}
|
||||
goto update_cache;
|
||||
}
|
||||
|
||||
if (!o->reset && seaf_stat (path, &st) == 0 && S_ISREG(st.st_mode) &&
|
||||
(ce->current_mtime != st.st_mtime))
|
||||
{
|
||||
/* If we're recovering an interrupted merge, we don't know whether
|
||||
* the file was changed by checkout or by the user. So we have to
|
||||
* calculate the sha1 for that file and compare it with the one in
|
||||
* cache entry.
|
||||
*/
|
||||
if (!recover_merge ||
|
||||
compare_file_content (path, &st, ce->sha1, o->crypt, o->version) != 0) {
|
||||
seaf_warning ("File %s is changed. Checkout to conflict file.\n", path);
|
||||
force_conflict = TRUE;
|
||||
} else {
|
||||
/* Recover merge and file content matches index entry.
|
||||
* We were interrupted before updating the index, update index
|
||||
* entry timestamp now.
|
||||
*/
|
||||
goto update_cache;
|
||||
}
|
||||
}
|
||||
|
||||
/* then checkout the file. */
|
||||
gboolean conflicted = FALSE;
|
||||
rawdata_to_hex (ce->sha1, file_id, 20);
|
||||
if (seaf_fs_manager_checkout_file (seaf->fs_mgr,
|
||||
o->repo_id,
|
||||
o->version,
|
||||
file_id,
|
||||
path,
|
||||
ce->ce_mode,
|
||||
ce->ce_mtime.sec,
|
||||
o->crypt,
|
||||
ce->name,
|
||||
conflict_head_id,
|
||||
force_conflict,
|
||||
&conflicted,
|
||||
NULL) < 0) {
|
||||
seaf_warning ("Failed to checkout file %s.\n", path);
|
||||
g_free (path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* If case conflict, this file has been checked out to another path.
|
||||
* Remove the current entry, otherwise it won't be removed later
|
||||
* since it's timestamp is 0.
|
||||
*/
|
||||
if (case_conflict) {
|
||||
ce->ce_flags |= CE_REMOVE;
|
||||
g_free (path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (conflicted) {
|
||||
g_free (path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
update_cache:
|
||||
/* finally fill cache_entry info */
|
||||
/* Only update index if we checked out the file without any error
|
||||
* or conflicts. The timestamp of the entry will remain 0 if error
|
||||
* or conflicted.
|
||||
*/
|
||||
seaf_stat (path, &st);
|
||||
fill_stat_cache_info (ce, &st);
|
||||
|
||||
g_free (path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
update_worktree (struct unpack_trees_options *o,
|
||||
gboolean recover_merge,
|
||||
const char *conflict_head_id,
|
||||
const char *default_conflict_suffix,
|
||||
int *finished_entries)
|
||||
{
|
||||
struct index_state *result = &o->result;
|
||||
int i;
|
||||
struct cache_entry *ce;
|
||||
int errs = 0;
|
||||
GHashTable *conflict_hash, *no_conflict_hash;
|
||||
|
||||
for (i = 0; i < result->cache_nr; ++i) {
|
||||
ce = result->cache[i];
|
||||
if (ce->ce_flags & CE_WT_REMOVE)
|
||||
errs |= unlink_entry (ce, o);
|
||||
}
|
||||
|
||||
conflict_hash = g_hash_table_new_full (g_str_hash, g_str_equal,
|
||||
g_free, g_free);
|
||||
no_conflict_hash = g_hash_table_new_full (g_str_hash, g_str_equal,
|
||||
g_free, NULL);
|
||||
|
||||
for (i = 0; i < result->cache_nr; ++i) {
|
||||
ce = result->cache[i];
|
||||
if (ce->ce_flags & CE_UPDATE) {
|
||||
errs |= checkout_entry (ce, o, recover_merge,
|
||||
conflict_head_id,
|
||||
conflict_hash, no_conflict_hash);
|
||||
}
|
||||
if (finished_entries)
|
||||
*finished_entries = *finished_entries + 1;
|
||||
}
|
||||
|
||||
g_hash_table_destroy (conflict_hash);
|
||||
g_hash_table_destroy (no_conflict_hash);
|
||||
|
||||
if (errs != 0)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
delete_path (const char *worktree, const char *name,
|
||||
unsigned int mode, gint64 old_mtime)
|
||||
@ -1160,37 +635,3 @@ files_locked_on_windows (struct index_state *index, const char *worktree)
|
||||
|
||||
#endif /* WIN32 */
|
||||
|
||||
void
|
||||
fill_seafile_blocks (const char *repo_id, int version,
|
||||
const unsigned char *sha1, BlockList *bl)
|
||||
{
|
||||
char file_id[41];
|
||||
Seafile *seafile;
|
||||
int i;
|
||||
|
||||
rawdata_to_hex (sha1, file_id, 20);
|
||||
seafile = seaf_fs_manager_get_seafile (seaf->fs_mgr, repo_id, version, file_id);
|
||||
if (!seafile) {
|
||||
seaf_warning ("Failed to find file %s.\n", file_id);
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < seafile->n_blocks; ++i)
|
||||
block_list_insert (bl, seafile->blk_sha1s[i]);
|
||||
|
||||
seafile_unref (seafile);
|
||||
}
|
||||
|
||||
void
|
||||
collect_new_blocks_from_index (const char *repo_id, int version,
|
||||
struct index_state *index, BlockList *bl)
|
||||
{
|
||||
int i;
|
||||
struct cache_entry *ce;
|
||||
|
||||
for (i = 0; i < index->cache_nr; ++i) {
|
||||
ce = index->cache[i];
|
||||
if (ce->ce_flags & CE_UPDATE)
|
||||
fill_seafile_blocks (repo_id, version, ce->sha1, bl);
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,6 @@
|
||||
|
||||
#include "index/index.h"
|
||||
#include "index/cache-tree.h"
|
||||
#include "unpack-trees.h"
|
||||
#include "fs-mgr.h"
|
||||
|
||||
#define PATH_SEPERATOR "/"
|
||||
@ -28,24 +27,9 @@ commit_trees_cb (const char *repo_id, int version,
|
||||
int
|
||||
update_index (struct index_state *istate, const char *index_path);
|
||||
|
||||
int
|
||||
update_worktree (struct unpack_trees_options *o,
|
||||
gboolean recover_merge,
|
||||
const char *conflict_head,
|
||||
const char *default_conflict_suffix,
|
||||
int *finished_entries);
|
||||
|
||||
int
|
||||
seaf_remove_empty_dir (const char *path);
|
||||
|
||||
char *
|
||||
build_case_conflict_free_path (const char *worktree,
|
||||
const char *ce_name,
|
||||
GHashTable *conflict_hash,
|
||||
GHashTable *no_conflict_hash,
|
||||
gboolean *is_case_conflict,
|
||||
gboolean is_rename);
|
||||
|
||||
char *
|
||||
build_checkout_path (const char *worktree, const char *ce_name, int len);
|
||||
|
||||
@ -53,16 +37,6 @@ int
|
||||
delete_path (const char *worktree, const char *name,
|
||||
unsigned int mode, gint64 old_mtime);
|
||||
|
||||
struct index_state;
|
||||
|
||||
int
|
||||
delete_dir_with_check (const char *repo_id,
|
||||
int repo_version,
|
||||
const char *root_id,
|
||||
const char *dir_path,
|
||||
const char *worktree,
|
||||
struct index_state *istate);
|
||||
|
||||
gboolean
|
||||
do_check_file_locked (const char *path, const char *worktree, gboolean locked_on_server);
|
||||
|
||||
@ -78,12 +52,4 @@ compare_file_content (const char *path, SeafStat *st,
|
||||
struct SeafileCrypt *crypt,
|
||||
int repo_version);
|
||||
|
||||
void
|
||||
fill_seafile_blocks (const char *repo_id, int version,
|
||||
const unsigned char *sha1, BlockList *bl);
|
||||
|
||||
void
|
||||
collect_new_blocks_from_index (const char *repo_id, int version,
|
||||
struct index_state *index, BlockList *bl);
|
||||
|
||||
#endif
|
||||
|
@ -479,7 +479,7 @@ wt_monitor_job_linux (void *vmonitor)
|
||||
}
|
||||
|
||||
if (FD_ISSET (monitor->cmd_pipe[0], &fds)) {
|
||||
n = pipereadn (monitor->cmd_pipe[0], &cmd, sizeof(cmd));
|
||||
n = seaf_pipe_readn (monitor->cmd_pipe[0], &cmd, sizeof(cmd));
|
||||
if (n != sizeof(cmd)) {
|
||||
seaf_warning ("[wt mon] failed to read command.\n");
|
||||
continue;
|
||||
@ -679,7 +679,7 @@ reply_watch_command (SeafWTMonitor *monitor, int result)
|
||||
{
|
||||
int n;
|
||||
|
||||
n = pipewriten (monitor->res_pipe[1], &result, sizeof(int));
|
||||
n = seaf_pipe_writen (monitor->res_pipe[1], &result, sizeof(int));
|
||||
if (n != sizeof(int))
|
||||
seaf_warning ("[wt mon] fail to write command result.\n");
|
||||
}
|
||||
|
@ -9,7 +9,7 @@
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <ccnet/job-mgr.h>
|
||||
#include "job-mgr.h"
|
||||
#include "seafile-session.h"
|
||||
#include "utils.h"
|
||||
#include "wt-monitor.h"
|
||||
@ -319,7 +319,7 @@ command_read_cb (CFFileDescriptorRef fdref,
|
||||
WatchCommand cmd;
|
||||
int n;
|
||||
|
||||
n = pipereadn (monitor->cmd_pipe[0], &cmd, sizeof(cmd));
|
||||
n = seaf_pipe_readn (monitor->cmd_pipe[0], &cmd, sizeof(cmd));
|
||||
if (n != sizeof(cmd)) {
|
||||
seaf_warning ("[wt mon] failed to read command.\n");
|
||||
CFFileDescriptorEnableCallBacks (fdref, kCFFileDescriptorReadCallBack);
|
||||
@ -398,7 +398,7 @@ reply_watch_command (SeafWTMonitor *monitor, int result)
|
||||
{
|
||||
int n;
|
||||
|
||||
n = pipewriten (monitor->res_pipe[1], &result, sizeof(int));
|
||||
n = seaf_pipe_writen (monitor->res_pipe[1], &result, sizeof(int));
|
||||
if (n != sizeof(int))
|
||||
seaf_warning ("[wt mon] fail to write command result.\n");
|
||||
}
|
||||
|
@ -733,7 +733,7 @@ reply_watch_command (SeafWTMonitor *monitor, int result)
|
||||
{
|
||||
int n;
|
||||
|
||||
n = pipewriten (monitor->res_pipe[1], &result, sizeof(int));
|
||||
n = seaf_pipe_writen (monitor->res_pipe[1], &result, sizeof(int));
|
||||
if (n != sizeof(int))
|
||||
seaf_warning ("[wt mon] fail to write command result.\n");
|
||||
}
|
||||
|
@ -9,26 +9,26 @@
|
||||
#define DEBUG_FLAG SEAFILE_DEBUG_WATCH
|
||||
#include "log.h"
|
||||
|
||||
#include <ccnet/job-mgr.h>
|
||||
#include "job-mgr.h"
|
||||
|
||||
int
|
||||
seaf_wt_monitor_start (SeafWTMonitor *monitor)
|
||||
{
|
||||
if (ccnet_pipe (monitor->cmd_pipe) < 0) {
|
||||
if (seaf_pipe (monitor->cmd_pipe) < 0) {
|
||||
seaf_warning ("[wt mon] failed to create command pipe: %s.\n",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ccnet_pipe (monitor->res_pipe) < 0) {
|
||||
if (seaf_pipe (monitor->res_pipe) < 0) {
|
||||
seaf_warning ("[wt mon] failed to create result pipe: %s.\n",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ccnet_job_manager_schedule_job (monitor->seaf->job_mgr,
|
||||
monitor->job_func,
|
||||
NULL, monitor) < 0) {
|
||||
if (seaf_job_manager_schedule_job (monitor->seaf->job_mgr,
|
||||
monitor->job_func,
|
||||
NULL, monitor) < 0) {
|
||||
seaf_warning ("[wt mon] failed to start monitor thread.\n");
|
||||
return -1;
|
||||
}
|
||||
@ -49,7 +49,7 @@ seaf_wt_monitor_watch_repo (SeafWTMonitor *monitor,
|
||||
cmd.type = CMD_ADD_WATCH;
|
||||
g_strlcpy (cmd.worktree, worktree, SEAF_PATH_MAX);
|
||||
|
||||
int n = pipewriten (monitor->cmd_pipe[1], &cmd, sizeof(cmd));
|
||||
int n = seaf_pipe_writen (monitor->cmd_pipe[1], &cmd, sizeof(cmd));
|
||||
|
||||
if (n != sizeof(cmd)) {
|
||||
seaf_warning ("[wt mon] fail to write command pipe.\n");
|
||||
@ -58,7 +58,7 @@ seaf_wt_monitor_watch_repo (SeafWTMonitor *monitor,
|
||||
|
||||
seaf_debug ("send a watch command, repo %s\n", repo_id);
|
||||
|
||||
n = pipereadn (monitor->res_pipe[0], &res, sizeof(int));
|
||||
n = seaf_pipe_readn (monitor->res_pipe[0], &res, sizeof(int));
|
||||
if (n != sizeof(int)) {
|
||||
seaf_warning ("[wt mon] fail to read result pipe.\n");
|
||||
return -1;
|
||||
@ -77,7 +77,7 @@ seaf_wt_monitor_unwatch_repo (SeafWTMonitor *monitor, const char *repo_id)
|
||||
memcpy (cmd.repo_id, repo_id, 37);
|
||||
cmd.type = CMD_DELETE_WATCH;
|
||||
|
||||
int n = pipewriten (monitor->cmd_pipe[1], &cmd, sizeof(cmd));
|
||||
int n = seaf_pipe_writen (monitor->cmd_pipe[1], &cmd, sizeof(cmd));
|
||||
|
||||
if (n != sizeof(cmd)) {
|
||||
seaf_warning ("[wt mon] fail to write command pipe.\n");
|
||||
@ -86,7 +86,7 @@ seaf_wt_monitor_unwatch_repo (SeafWTMonitor *monitor, const char *repo_id)
|
||||
|
||||
seaf_debug ("send an unwatch command, repo %s\n", repo_id);
|
||||
|
||||
n = pipereadn (monitor->res_pipe[0], &res, sizeof(int));
|
||||
n = seaf_pipe_readn (monitor->res_pipe[0], &res, sizeof(int));
|
||||
if (n != sizeof(int)) {
|
||||
seaf_warning ("[wt mon] fail to read result pipe.\n");
|
||||
return -1;
|
||||
@ -105,7 +105,7 @@ seaf_wt_monitor_refresh_repo (SeafWTMonitor *monitor, const char *repo_id)
|
||||
memcpy (cmd.repo_id, repo_id, 37);
|
||||
cmd.type = CMD_REFRESH_WATCH;
|
||||
|
||||
int n = pipewriten (monitor->cmd_pipe[1], &cmd, sizeof(cmd));
|
||||
int n = seaf_pipe_writen (monitor->cmd_pipe[1], &cmd, sizeof(cmd));
|
||||
|
||||
if (n != sizeof(cmd)) {
|
||||
seaf_warning ("[wt mon] fail to write command pipe.\n");
|
||||
@ -114,7 +114,7 @@ seaf_wt_monitor_refresh_repo (SeafWTMonitor *monitor, const char *repo_id)
|
||||
|
||||
seaf_debug ("send a refresh command, repo %s\n", repo_id);
|
||||
|
||||
n = pipereadn (monitor->res_pipe[0], &res, sizeof(int));
|
||||
n = seaf_pipe_readn (monitor->res_pipe[0], &res, sizeof(int));
|
||||
if (n != sizeof(int)) {
|
||||
seaf_warning ("[wt mon] fail to read result pipe.\n");
|
||||
return -1;
|
||||
|
@ -25,8 +25,8 @@ typedef struct SeafWTMonitor {
|
||||
struct _SeafileSession *seaf;
|
||||
SeafWTMonitorPriv *priv;
|
||||
|
||||
ccnet_pipe_t cmd_pipe[2];
|
||||
ccnet_pipe_t res_pipe[2];
|
||||
seaf_pipe_t cmd_pipe[2];
|
||||
seaf_pipe_t res_pipe[2];
|
||||
|
||||
/* platform dependent virtual functions */
|
||||
void* (*job_func) (void *);
|
||||
|
@ -2,6 +2,7 @@
|
||||
#ifndef _SEAFILE_RPC_H
|
||||
#define _SEAFILE_RPC_H
|
||||
|
||||
#include <jansson.h>
|
||||
#include "seafile-object.h"
|
||||
|
||||
/**
|
||||
@ -19,57 +20,6 @@ seafile_get_session_info (GError **error);
|
||||
*/
|
||||
GList* seafile_get_repo_list (int start, int limit, GError **error);
|
||||
|
||||
gint64
|
||||
seafile_count_repos (GError **error);
|
||||
|
||||
/**
|
||||
* seafile_get_trash_repo_list:
|
||||
*
|
||||
* Returns deleted repository list.
|
||||
*/
|
||||
GList* seafile_get_trash_repo_list(int start, int limit, GError **error);
|
||||
|
||||
int
|
||||
seafile_del_repo_from_trash (const char *repo_id, GError **error);
|
||||
|
||||
int
|
||||
seafile_restore_repo_from_trash (const char *repo_id, GError **error);
|
||||
|
||||
GList *
|
||||
seafile_get_trash_repos_by_owner (const char *owner, GError **error);
|
||||
|
||||
int
|
||||
seafile_empty_repo_trash (GError **error);
|
||||
|
||||
int
|
||||
seafile_empty_repo_trash_by_owner (const char *owner, GError **error);
|
||||
|
||||
/**
|
||||
* seafile_get_commit_list:
|
||||
*
|
||||
* @limit: if limit <= 0, all commits start from @offset will be returned.
|
||||
*
|
||||
* Returns: commit list of a given repo.
|
||||
*
|
||||
* Possible Error:
|
||||
* 1. Bad Argument
|
||||
* 2. No head and branch master
|
||||
* 3. Failed to list commits
|
||||
*/
|
||||
GList* seafile_get_commit_list (const gchar *repo,
|
||||
int offset,
|
||||
int limit,
|
||||
GError **error);
|
||||
|
||||
/**
|
||||
* seafile_get_commit:
|
||||
* @id: the commit id.
|
||||
*
|
||||
* Returns: the commit object.
|
||||
*/
|
||||
GObject* seafile_get_commit (const char *repo_id, int version,
|
||||
const gchar *id, GError **error);
|
||||
|
||||
/**
|
||||
* seafile_get_repo:
|
||||
*
|
||||
@ -131,49 +81,9 @@ seafile_get_download_rate(GError **error);
|
||||
int
|
||||
seafile_get_upload_rate(GError **error);
|
||||
|
||||
/**
|
||||
* seafile_edit_repo:
|
||||
* @repo_id: repository id.
|
||||
* @name: new name of the repository, NULL if unchanged.
|
||||
* @description: new description of the repository, NULL if unchanged.
|
||||
*/
|
||||
int seafile_edit_repo (const gchar *repo_id,
|
||||
const gchar *name,
|
||||
const gchar *description,
|
||||
const gchar *user,
|
||||
GError **error);
|
||||
|
||||
int
|
||||
seafile_change_repo_passwd (const char *repo_id,
|
||||
const char *old_passwd,
|
||||
const char *new_passwd,
|
||||
const char *user,
|
||||
GError **error);
|
||||
|
||||
/**
|
||||
* seafile_repo_size:
|
||||
*
|
||||
* Returns: the size of a repo
|
||||
*
|
||||
* Possible Error:
|
||||
* 1. Bad Argument
|
||||
* 2. No local branch (No local branch record in branch.db)
|
||||
* 3. Database error
|
||||
* 4. Calculate branch size error
|
||||
*/
|
||||
gint64
|
||||
seafile_repo_size(const gchar *repo_id, GError **error);
|
||||
|
||||
int
|
||||
seafile_repo_last_modify(const char *repo_id, GError **error);
|
||||
|
||||
int seafile_set_repo_lantoken (const gchar *repo_id,
|
||||
const gchar *token,
|
||||
GError **error);
|
||||
|
||||
gchar* seafile_get_repo_lantoken (const gchar *repo_id,
|
||||
GError **error);
|
||||
|
||||
int
|
||||
seafile_set_repo_property (const char *repo_id,
|
||||
const char *key,
|
||||
@ -185,20 +95,6 @@ seafile_get_repo_property (const char *repo_id,
|
||||
const char *key,
|
||||
GError **error);
|
||||
|
||||
char *
|
||||
seafile_get_repo_relay_address (const char *repo_id,
|
||||
GError **error);
|
||||
|
||||
char *
|
||||
seafile_get_repo_relay_port (const char *repo_id,
|
||||
GError **error);
|
||||
|
||||
int
|
||||
seafile_update_repo_relay_info (const char *repo_id,
|
||||
const char *new_addr,
|
||||
const char *new_port,
|
||||
GError **error);
|
||||
|
||||
int
|
||||
seafile_update_repos_server_host (const char *old_host,
|
||||
const char *new_host,
|
||||
@ -235,56 +131,6 @@ seafile_set_server_property (const char *server_url,
|
||||
GList *
|
||||
seafile_get_file_sync_errors (int offset, int limit, GError **error);
|
||||
|
||||
/**
|
||||
* seafile_list_dir:
|
||||
* List a directory.
|
||||
*
|
||||
* Returns: a list of dirents.
|
||||
*
|
||||
* @limit: if limit <= 0, all dirents start from @offset will be returned.
|
||||
*/
|
||||
GList * seafile_list_dir (const char *repo_id,
|
||||
const char *dir_id, int offset, int limit, GError **error);
|
||||
|
||||
/**
|
||||
* seafile_list_file_blocks:
|
||||
* List the blocks of a file.
|
||||
*
|
||||
* Returns: a list of block ids speprated by '\n'.
|
||||
*
|
||||
* @limit: if limit <= 0, all blocks start from @offset will be returned.
|
||||
*/
|
||||
char * seafile_list_file_blocks (const char *repo_id,
|
||||
const char *file_id,
|
||||
int offset, int limit,
|
||||
GError **error);
|
||||
|
||||
/**
|
||||
* seafile_list_dir_by_path:
|
||||
* List a directory in a commit by the path of the directory.
|
||||
*
|
||||
* Returns: a list of dirents.
|
||||
*/
|
||||
GList * seafile_list_dir_by_path (const char *repo_id,
|
||||
const char *commit_id, const char *path, GError **error);
|
||||
|
||||
/**
|
||||
* seafile_get_dir_id_by_commit_and_path:
|
||||
* Get the dir_id of the path
|
||||
*
|
||||
* Returns: the dir_id of the path
|
||||
*/
|
||||
char * seafile_get_dir_id_by_commit_and_path (const char *repo_id,
|
||||
const char *commit_id,
|
||||
const char *path,
|
||||
GError **error);
|
||||
|
||||
/**
|
||||
* seafile_revert:
|
||||
* Reset the repo to a previous state by creating a new commit.
|
||||
*/
|
||||
int seafile_revert (const char *repo_id, const char *commit, GError **error);
|
||||
|
||||
char *
|
||||
seafile_gen_default_worktree (const char *worktree_parent,
|
||||
const char *repo_name,
|
||||
@ -352,46 +198,6 @@ seafile_get_clone_tasks (GError **error);
|
||||
*/
|
||||
int seafile_sync (const char *repo_id, const char *peer_id, GError **error);
|
||||
|
||||
/**
|
||||
* seafile_get_total_block_size:
|
||||
*
|
||||
* Get the sum of size of all the blocks.
|
||||
*/
|
||||
gint64
|
||||
seafile_get_total_block_size (GError **error);
|
||||
|
||||
|
||||
/**
|
||||
* seafile_get_commit_tree_block_number:
|
||||
*
|
||||
* Get the number of blocks belong to the commit tree.
|
||||
*
|
||||
* @commit_id: the head of the commit tree.
|
||||
*
|
||||
* Returns: -1 if the calculation is in progress, -2 if error, >=0 otherwise.
|
||||
*/
|
||||
int
|
||||
seafile_get_commit_tree_block_number (const char *commit_id, GError **error);
|
||||
|
||||
|
||||
/**
|
||||
* seafile_gc:
|
||||
* Start garbage collection.
|
||||
*/
|
||||
int
|
||||
seafile_gc (GError **error);
|
||||
|
||||
/**
|
||||
* seafile_gc_get_progress:
|
||||
* Get progress of GC.
|
||||
*
|
||||
* Returns:
|
||||
* progress of GC in precentage.
|
||||
* -1 if GC is not running.
|
||||
*/
|
||||
/* int */
|
||||
/* seafile_gc_get_progress (GError **error); */
|
||||
|
||||
/* ----------------- Task Related -------------- */
|
||||
|
||||
/**
|
||||
@ -425,608 +231,10 @@ GList *
|
||||
seafile_diff (const char *repo_id, const char *old, const char *new,
|
||||
int fold_dir_diff, GError **error);
|
||||
|
||||
GList *
|
||||
seafile_branch_gets (const char *repo_id, GError **error);
|
||||
|
||||
/**
|
||||
* Return 1 if user is the owner of repo, otherwise return 0.
|
||||
*/
|
||||
int
|
||||
seafile_is_repo_owner (const char *email, const char *repo_id,
|
||||
GError **error);
|
||||
|
||||
int
|
||||
seafile_set_repo_owner(const char *repo_id, const char *email,
|
||||
GError **error);
|
||||
|
||||
/**
|
||||
* Return owner id of repo
|
||||
*/
|
||||
char *
|
||||
seafile_get_repo_owner(const char *repo_id, GError **error);
|
||||
|
||||
GList *
|
||||
seafile_get_orphan_repo_list(GError **error);
|
||||
|
||||
GList *
|
||||
seafile_list_owned_repos (const char *email, int ret_corrupted, GError **error);
|
||||
|
||||
/**
|
||||
* seafile_add_chunk_server:
|
||||
* @server: ID for the chunk server.
|
||||
*
|
||||
* Add a chunk server on a relay server.
|
||||
*/
|
||||
int seafile_add_chunk_server (const char *server, GError **error);
|
||||
|
||||
/**
|
||||
* seafile_del_chunk_server:
|
||||
* @server: ID for the chunk server.
|
||||
*
|
||||
* Delete a chunk server on a relay server.
|
||||
*/
|
||||
int seafile_del_chunk_server (const char *server, GError **error);
|
||||
|
||||
/**
|
||||
* seafile_list_chunk_servers:
|
||||
*
|
||||
* List chunk servers set on a relay server.
|
||||
*/
|
||||
char *seafile_list_chunk_servers (GError **error);
|
||||
|
||||
gint64 seafile_get_user_quota_usage (const char *email, GError **error);
|
||||
|
||||
gint64 seafile_get_user_share_usage (const char *email, GError **error);
|
||||
|
||||
gint64
|
||||
seafile_server_repo_size(const char *repo_id, GError **error);
|
||||
|
||||
int
|
||||
seafile_repo_set_access_property (const char *repo_id, const char *ap,
|
||||
GError **error);
|
||||
|
||||
char *
|
||||
seafile_repo_query_access_property (const char *repo_id, GError **error);
|
||||
|
||||
char *
|
||||
seafile_web_get_access_token (const char *repo_id,
|
||||
const char *obj_id,
|
||||
const char *op,
|
||||
const char *username,
|
||||
int use_onetime,
|
||||
GError **error);
|
||||
|
||||
GObject *
|
||||
seafile_web_query_access_token (const char *token, GError **error);
|
||||
|
||||
char *
|
||||
seafile_query_zip_progress (const char *token, GError **error);
|
||||
|
||||
GObject *
|
||||
seafile_get_checkout_task (const char *repo_id, GError **error);
|
||||
|
||||
GList *
|
||||
seafile_get_sync_task_list (GError **error);
|
||||
|
||||
int
|
||||
seafile_share_subdir_to_user (const char *repo_id,
|
||||
const char *path,
|
||||
const char *owner,
|
||||
const char *share_user,
|
||||
const char *permission,
|
||||
const char *passwd,
|
||||
GError **error);
|
||||
|
||||
int
|
||||
seafile_unshare_subdir_for_user (const char *repo_id,
|
||||
const char *path,
|
||||
const char *owner,
|
||||
const char *share_user,
|
||||
GError **error);
|
||||
|
||||
int
|
||||
seafile_update_share_subdir_perm_for_user (const char *repo_id,
|
||||
const char *path,
|
||||
const char *owner,
|
||||
const char *share_user,
|
||||
const char *permission,
|
||||
GError **error);
|
||||
|
||||
int
|
||||
seafile_add_share (const char *repo_id, const char *from_email,
|
||||
const char *to_email, const char *permission,
|
||||
GError **error);
|
||||
|
||||
GList *
|
||||
seafile_list_share_repos (const char *email, const char *type,
|
||||
int start, int limit, GError **error);
|
||||
|
||||
GList *
|
||||
seafile_list_repo_shared_to (const char *from_user, const char *repo_id,
|
||||
GError **error);
|
||||
|
||||
GList *
|
||||
seafile_list_repo_shared_group (const char *from_user, const char *repo_id,
|
||||
GError **error);
|
||||
|
||||
int
|
||||
seafile_remove_share (const char *repo_id, const char *from_email,
|
||||
const char *to_email, GError **error);
|
||||
|
||||
int
|
||||
seafile_share_subdir_to_group (const char *repo_id,
|
||||
const char *path,
|
||||
const char *owner,
|
||||
int share_group,
|
||||
const char *permission,
|
||||
const char *passwd,
|
||||
GError **error);
|
||||
|
||||
int
|
||||
seafile_unshare_subdir_for_group (const char *repo_id,
|
||||
const char *path,
|
||||
const char *owner,
|
||||
int share_group,
|
||||
GError **error);
|
||||
|
||||
int
|
||||
seafile_update_share_subdir_perm_for_group (const char *repo_id,
|
||||
const char *path,
|
||||
const char *owner,
|
||||
int share_group,
|
||||
const char *permission,
|
||||
GError **error);
|
||||
|
||||
int
|
||||
seafile_group_share_repo (const char *repo_id, int group_id,
|
||||
const char *user_name, const char *permission,
|
||||
GError **error);
|
||||
int
|
||||
seafile_group_unshare_repo (const char *repo_id, int group_id,
|
||||
const char *user_name, GError **error);
|
||||
|
||||
/* Get groups that a repo is shared to */
|
||||
char *
|
||||
seafile_get_shared_groups_by_repo(const char *repo_id, GError **error);
|
||||
|
||||
char *
|
||||
seafile_get_group_repoids (int group_id, GError **error);
|
||||
|
||||
GList *
|
||||
seafile_get_repos_by_group (int group_id, GError **error);
|
||||
|
||||
GList *
|
||||
seafile_get_group_repos_by_owner (char *user, GError **error);
|
||||
|
||||
char *
|
||||
seafile_get_group_repo_owner (const char *repo_id, GError **error);
|
||||
|
||||
int
|
||||
seafile_remove_repo_group(int group_id, const char *username, GError **error);
|
||||
|
||||
gint64
|
||||
seafile_get_file_size (const char *store_id, int version,
|
||||
const char *file_id, GError **error);
|
||||
|
||||
gint64
|
||||
seafile_get_dir_size (const char *store_id, int version,
|
||||
const char *dir_id, GError **error);
|
||||
|
||||
int
|
||||
seafile_set_repo_history_limit (const char *repo_id,
|
||||
int days,
|
||||
GError **error);
|
||||
|
||||
int
|
||||
seafile_get_repo_history_limit (const char *repo_id,
|
||||
GError **error);
|
||||
|
||||
int
|
||||
seafile_check_passwd (const char *repo_id,
|
||||
const char *magic,
|
||||
GError **error);
|
||||
|
||||
int
|
||||
seafile_set_passwd (const char *repo_id,
|
||||
const char *user,
|
||||
const char *passwd,
|
||||
GError **error);
|
||||
|
||||
int
|
||||
seafile_unset_passwd (const char *repo_id,
|
||||
const char *user,
|
||||
GError **error);
|
||||
|
||||
int
|
||||
seafile_is_passwd_set (const char *repo_id, const char *user, GError **error);
|
||||
|
||||
GObject *
|
||||
seafile_get_decrypt_key (const char *repo_id, const char *user, GError **error);
|
||||
|
||||
int
|
||||
seafile_revert_on_server (const char *repo_id,
|
||||
const char *commit_id,
|
||||
const char *user_name,
|
||||
GError **error);
|
||||
|
||||
/**
|
||||
* Add a file into the repo on server.
|
||||
* The content of the file is stored in a temporary file.
|
||||
* @repo_id: repo id
|
||||
* @temp_file_path: local file path, should be a temp file just uploaded.
|
||||
* @parent_dir: the parent directory to put the file in.
|
||||
* @file_name: the name of the target file.
|
||||
* @user: the email of the user who uploaded the file.
|
||||
*/
|
||||
int
|
||||
seafile_post_file (const char *repo_id, const char *temp_file_path,
|
||||
const char *parent_dir, const char *file_name,
|
||||
const char *user,
|
||||
GError **error);
|
||||
|
||||
/**
|
||||
* Add multiple files at once.
|
||||
*
|
||||
* @filenames_json: json array of filenames
|
||||
* @paths_json: json array of temp file paths
|
||||
*/
|
||||
char *
|
||||
seafile_post_multi_files (const char *repo_id,
|
||||
const char *parent_dir,
|
||||
const char *filenames_json,
|
||||
const char *paths_json,
|
||||
const char *user,
|
||||
int replace,
|
||||
GError **error);
|
||||
|
||||
/**
|
||||
* Add file blocks at once.
|
||||
*
|
||||
* @blocks_json: json array of block ids
|
||||
* @paths_json: json array of temp file paths
|
||||
*/
|
||||
char *
|
||||
seafile_post_file_blocks (const char *repo_id,
|
||||
const char *parent_dir,
|
||||
const char *file_name,
|
||||
const char *blockids_json,
|
||||
const char *paths_json,
|
||||
const char *user,
|
||||
gint64 file_size,
|
||||
int replace_existed,
|
||||
GError **error);
|
||||
|
||||
|
||||
int
|
||||
seafile_post_empty_file (const char *repo_id, const char *parent_dir,
|
||||
const char *new_file_name, const char *user,
|
||||
GError **error);
|
||||
|
||||
/**
|
||||
* Update an existing file in a repo
|
||||
* @params: same as seafile_post_file
|
||||
* @head_id: the commit id for the original file version.
|
||||
* It's optional. If it's NULL, the current repo head will be used.
|
||||
* @return The new file id
|
||||
*/
|
||||
char *
|
||||
seafile_put_file (const char *repo_id, const char *temp_file_path,
|
||||
const char *parent_dir, const char *file_name,
|
||||
const char *user, const char *head_id,
|
||||
GError **error);
|
||||
|
||||
/**
|
||||
* Add file blocks at once.
|
||||
*
|
||||
* @blocks_json: json array of block ids
|
||||
* @paths_json: json array of temp file paths
|
||||
*/
|
||||
char *
|
||||
seafile_put_file_blocks (const char *repo_id, const char *parent_dir,
|
||||
const char *file_name, const char *blockids_json,
|
||||
const char *paths_json, const char *user,
|
||||
const char *head_id, gint64 file_size, GError **error);
|
||||
|
||||
|
||||
int
|
||||
seafile_post_dir (const char *repo_id, const char *parent_dir,
|
||||
const char *new_dir_name, const char *user,
|
||||
GError **error);
|
||||
|
||||
/**
|
||||
* delete a file/directory from the repo on server.
|
||||
* @repo_id: repo id
|
||||
* @parent_dir: the parent directory of the file to be deleted
|
||||
* @file_name: the name of the target file.
|
||||
* @user: the email of the user who uploaded the file.
|
||||
*/
|
||||
int
|
||||
seafile_del_file (const char *repo_id,
|
||||
const char *parent_dir, const char *file_name,
|
||||
const char *user,
|
||||
GError **error);
|
||||
|
||||
/**
|
||||
* copy a file/directory from a repo to another on server.
|
||||
*/
|
||||
GObject *
|
||||
seafile_copy_file (const char *src_repo_id,
|
||||
const char *src_dir,
|
||||
const char *src_filename,
|
||||
const char *dst_repo_id,
|
||||
const char *dst_dir,
|
||||
const char *dst_filename,
|
||||
const char *user,
|
||||
int need_progress,
|
||||
int synchronous,
|
||||
GError **error);
|
||||
|
||||
|
||||
GObject *
|
||||
seafile_move_file (const char *src_repo_id,
|
||||
const char *src_dir,
|
||||
const char *src_filename,
|
||||
const char *dst_repo_id,
|
||||
const char *dst_dir,
|
||||
const char *dst_filename,
|
||||
int replace,
|
||||
const char *user,
|
||||
int need_progress,
|
||||
int synchronous,
|
||||
GError **error);
|
||||
|
||||
GObject *
|
||||
seafile_get_copy_task (const char *task_id, GError **error);
|
||||
|
||||
int
|
||||
seafile_cancel_copy_task (const char *task_id, GError **error);
|
||||
|
||||
int
|
||||
seafile_rename_file (const char *repo_id,
|
||||
const char *parent_dir,
|
||||
const char *oldname,
|
||||
const char *newname,
|
||||
const char *user,
|
||||
GError **error);
|
||||
|
||||
/**
|
||||
* Return non-zero if filename is valid.
|
||||
*/
|
||||
int
|
||||
seafile_is_valid_filename (const char *repo_id,
|
||||
const char *filename,
|
||||
GError **error);
|
||||
|
||||
|
||||
int
|
||||
seafile_set_user_quota (const char *user, gint64 quota, GError **error);
|
||||
|
||||
gint64
|
||||
seafile_get_user_quota (const char *user, GError **error);
|
||||
|
||||
int
|
||||
seafile_check_quota (const char *repo_id, GError **error);
|
||||
|
||||
char *
|
||||
seafile_get_file_id_by_path (const char *repo_id, const char *path,
|
||||
GError **error);
|
||||
|
||||
char *
|
||||
seafile_get_dir_id_by_path (const char *repo_id, const char *path,
|
||||
GError **error);
|
||||
|
||||
GObject *
|
||||
seafile_get_dirent_by_path (const char *repo_id, const char *path,
|
||||
GError **error);
|
||||
|
||||
/**
|
||||
* Return a list of commits where every commit contains a unique version of
|
||||
* the file.
|
||||
*/
|
||||
GList *
|
||||
seafile_list_file_revisions (const char *repo_id,
|
||||
const char *path,
|
||||
int max_revision,
|
||||
int limit,
|
||||
int show_days,
|
||||
GError **error);
|
||||
|
||||
GList *
|
||||
seafile_calc_files_last_modified (const char *repo_id,
|
||||
const char *parent_dir,
|
||||
int limit,
|
||||
GError **error);
|
||||
|
||||
int
|
||||
seafile_revert_file (const char *repo_id,
|
||||
const char *commit_id,
|
||||
const char *path,
|
||||
const char *user,
|
||||
GError **error);
|
||||
|
||||
int
|
||||
seafile_revert_dir (const char *repo_id,
|
||||
const char *commit_id,
|
||||
const char *path,
|
||||
const char *user,
|
||||
GError **error);
|
||||
|
||||
char *
|
||||
seafile_check_repo_blocks_missing (const char *repo_id,
|
||||
const char *blockids_json,
|
||||
GError **error);
|
||||
|
||||
/*
|
||||
* @show_days: return deleted files in how many days, return all if 0.
|
||||
*/
|
||||
GList *
|
||||
seafile_get_deleted (const char *repo_id, int show_days,
|
||||
const char *path, const char *scan_stat,
|
||||
int limit, GError **error);
|
||||
|
||||
/**
|
||||
* Generate a new token for (repo_id, email) and return it
|
||||
*/
|
||||
char *
|
||||
seafile_generate_repo_token (const char *repo_id,
|
||||
const char *email,
|
||||
GError **error);
|
||||
|
||||
int
|
||||
seafile_delete_repo_token (const char *repo_id,
|
||||
const char *token,
|
||||
const char *user,
|
||||
GError **error);
|
||||
|
||||
GList *
|
||||
seafile_list_repo_tokens (const char *repo_id,
|
||||
GError **error);
|
||||
|
||||
GList *
|
||||
seafile_list_repo_tokens_by_email (const char *email,
|
||||
GError **error);
|
||||
|
||||
int
|
||||
seafile_delete_repo_tokens_by_peer_id(const char *email, const char *peer_id, GError **error);
|
||||
|
||||
int
|
||||
seafile_delete_repo_tokens_by_email (const char *email,
|
||||
GError **error);
|
||||
|
||||
/**
|
||||
* create a repo on seahub
|
||||
*/
|
||||
char *
|
||||
seafile_create_repo (const char *repo_name,
|
||||
const char *repo_desc,
|
||||
const char *owner_email,
|
||||
const char *passwd,
|
||||
GError **error);
|
||||
|
||||
char *
|
||||
seafile_create_enc_repo (const char *repo_id,
|
||||
const char *repo_name,
|
||||
const char *repo_desc,
|
||||
const char *owner_email,
|
||||
const char *magic,
|
||||
const char *random_key,
|
||||
int enc_version,
|
||||
GError **error);
|
||||
|
||||
char *
|
||||
seafile_check_permission (const char *repo_id, const char *user, GError **error);
|
||||
|
||||
char *
|
||||
seafile_check_permission_by_path (const char *repo_id, const char *path,
|
||||
const char *user, GError **error);
|
||||
|
||||
GList *
|
||||
seafile_list_dir_with_perm (const char *repo_id,
|
||||
const char *path,
|
||||
const char *dir_id,
|
||||
const char *user,
|
||||
int offset,
|
||||
int limit,
|
||||
GError **error);
|
||||
|
||||
int
|
||||
seafile_set_inner_pub_repo (const char *repo_id,
|
||||
const char *permission,
|
||||
GError **error);
|
||||
|
||||
int
|
||||
seafile_unset_inner_pub_repo (const char *repo_id, GError **error);
|
||||
|
||||
GList *
|
||||
seafile_list_inner_pub_repos (GError **error);
|
||||
|
||||
gint64
|
||||
seafile_count_inner_pub_repos (GError **error);
|
||||
|
||||
GList *
|
||||
seafile_list_inner_pub_repos_by_owner (const char *user, GError **error);
|
||||
|
||||
int
|
||||
seafile_is_inner_pub_repo (const char *repo_id, GError **error);
|
||||
|
||||
int
|
||||
seafile_set_share_permission (const char *repo_id,
|
||||
const char *from_email,
|
||||
const char *to_email,
|
||||
const char *permission,
|
||||
GError **error);
|
||||
|
||||
int
|
||||
seafile_set_group_repo_permission (int group_id,
|
||||
const char *repo_id,
|
||||
const char *permission,
|
||||
GError **error);
|
||||
|
||||
char *
|
||||
seafile_get_file_id_by_commit_and_path(const char *repo_id,
|
||||
const char *commit_id,
|
||||
const char *path,
|
||||
GError **error);
|
||||
|
||||
/* virtual repo related */
|
||||
|
||||
char *
|
||||
seafile_create_virtual_repo (const char *origin_repo_id,
|
||||
const char *path,
|
||||
const char *repo_name,
|
||||
const char *repo_desc,
|
||||
const char *owner,
|
||||
const char *passwd,
|
||||
GError **error);
|
||||
|
||||
GList *
|
||||
seafile_get_virtual_repos_by_owner (const char *owner, GError **error);
|
||||
|
||||
GObject *
|
||||
seafile_get_virtual_repo (const char *origin_repo,
|
||||
const char *path,
|
||||
const char *owner,
|
||||
GError **error);
|
||||
|
||||
char *
|
||||
seafile_get_system_default_repo_id (GError **error);
|
||||
|
||||
/* Clean trash */
|
||||
|
||||
int
|
||||
seafile_clean_up_repo_history (const char *repo_id, int keep_days, GError **error);
|
||||
|
||||
/* ------------------ public RPC calls. ------------ */
|
||||
|
||||
GList* seafile_get_repo_list_pub (int start, int limit, GError **error);
|
||||
|
||||
GObject* seafile_get_repo_pub (const gchar* id, GError **error);
|
||||
|
||||
GList* seafile_get_commit_list_pub (const gchar *repo,
|
||||
int offset,
|
||||
int limit,
|
||||
GError **error);
|
||||
|
||||
GObject* seafile_get_commit_pub (const gchar *id, GError **error);
|
||||
|
||||
char *seafile_diff_pub (const char *repo_id, const char *old, const char *new,
|
||||
GError **error);
|
||||
|
||||
GList * seafile_list_dir_pub (const char *dir_id, GError **error);
|
||||
|
||||
GList *
|
||||
seafile_get_shared_users_for_subdir (const char *repo_id,
|
||||
const char *path,
|
||||
const char *from_user,
|
||||
GError **error);
|
||||
GList *
|
||||
seafile_get_shared_groups_for_subdir (const char *repo_id,
|
||||
const char *path,
|
||||
const char *from_user,
|
||||
GError **error);
|
||||
GObject *
|
||||
seafile_generate_magic_and_random_key(int enc_version,
|
||||
const char* repo_id,
|
||||
const char *passwd,
|
||||
GError **error);
|
||||
json_t * seafile_get_sync_notification (GError **error);
|
||||
#endif
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user