1
0
mirror of https://github.com/haiwen/seafile-server.git synced 2025-04-28 03:20:10 +00:00

Support copy/move files between enc repos (#611)

* Support copy/move files between enc repos

* Check block_id is empty

---------

Co-authored-by: 杨赫然 <heran.yang@seafile.com>
This commit is contained in:
feiniks 2023-05-05 11:34:23 +08:00 committed by GitHub
parent b558426da8
commit 30f26ef24f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -1895,9 +1895,195 @@ out:
return ret;
}
static int
write_block (const char *repo_id, const char *block_id, int version, const char *buf, int len)
{
SeafBlockManager *mgr = seaf->block_mgr;
BlockHandle *handle;
int n;
/* Don't write if the block already exists. */
if (seaf_block_manager_block_exists (mgr,
repo_id, version,
block_id)) {
return 0;
}
handle = seaf_block_manager_open_block (mgr,
repo_id, version,
block_id, BLOCK_WRITE);
if (!handle) {
seaf_warning ("Failed to open block %s.\n", block_id);
return -1;
}
n = seaf_block_manager_write_block (mgr, handle, buf, len);
if (n < 0) {
seaf_warning ("Failed to write block %s.\n", block_id);
seaf_block_manager_close_block (mgr, handle);
seaf_block_manager_block_handle_free (mgr, handle);
return -1;
}
if (seaf_block_manager_close_block (mgr, handle) < 0) {
seaf_warning ("failed to close block %s.\n", block_id);
seaf_block_manager_block_handle_free (mgr, handle);
return -1;
}
if (seaf_block_manager_commit_block (mgr, handle) < 0) {
seaf_warning ("failed to commit block %s.\n", block_id);
seaf_block_manager_block_handle_free (mgr, handle);
return -1;
}
seaf_block_manager_block_handle_free (mgr, handle);
return 0;
}
// return a new block id.
static char *
copy_seafile (SeafRepo *src_repo, SeafRepo *dst_repo, const char *file_id,
CopyTask *task, guint64 *size)
copy_block_between_enc_repo (SeafRepo *src_repo, SeafRepo *dst_repo,
SeafileCrypt *src_crypt, SeafileCrypt *dst_crypt,
const char *block_id)
{
SeafBlockManager *mgr = seaf->block_mgr;
char *ret = NULL;
BlockHandle *handle = NULL;
BlockMetadata *bmd = NULL;
char *buf = NULL;
char *src_dec_out = NULL;
int src_dec_out_len = -1;
SHA_CTX ctx;
uint8_t checksum[CHECKSUM_LENGTH];
char checksum_str[41];
int block_size = 0;
int n;
if (g_strcmp0 (block_id, EMPTY_SHA1) == 0) {
ret = g_strdup (block_id);
goto out;
}
// Read block from source repo.
handle = seaf_block_manager_open_block(mgr,
src_repo->store_id,
src_repo->version,
block_id, BLOCK_READ);
if (!handle) {
seaf_warning ("Failed to open block %s.\n", block_id);
return NULL;
}
bmd = seaf_block_manager_stat_block_by_handle (mgr, handle);
if (!bmd) {
seaf_warning ("Failed to stat block %s by handle.\n", block_id);
goto out;
}
block_size = bmd->size;
if (block_size == 0) {
ret = g_strdup (block_id);
goto out;
}
buf = g_new (char, block_size);
n = seaf_block_manager_read_block(seaf->block_mgr, handle, buf, block_size);
if (n != block_size) {
seaf_warning ("Failed to read block from source repo %s.\n", src_repo->id);
goto out;
}
if (src_crypt != NULL) {
int rc = seafile_decrypt (&src_dec_out, &src_dec_out_len,
buf, block_size, src_crypt);
if (rc != 0) {
seaf_warning ("Failed to decrypt block %s.\n", block_id);
goto out;
}
}
// Write block to destination repo.
if (src_crypt && dst_crypt) {
// Both source and destination repos are encrypted reops.
char *dst_enc_buf = NULL;
int dst_enc_len = -1;
int rc = seafile_encrypt (&dst_enc_buf, &dst_enc_len,
src_dec_out, src_dec_out_len, dst_crypt);
if (rc != 0) {
seaf_warning ("Failed to encrypt block for repo %s.\n", dst_repo->id);
goto out;
}
SHA1_Init (&ctx);
SHA1_Update (&ctx, dst_enc_buf, dst_enc_len);
SHA1_Final (checksum, &ctx);
rawdata_to_hex (checksum, checksum_str, 20);
if (write_block (dst_repo->store_id, checksum_str, dst_repo->version, dst_enc_buf, dst_enc_len) < 0) {
g_free (dst_enc_buf);
goto out;
}
g_free (dst_enc_buf);
ret = g_strdup (checksum_str);
} else if (src_crypt && !dst_crypt) {
// Source repo is encrypted.
SHA1_Init (&ctx);
SHA1_Update (&ctx, src_dec_out, src_dec_out_len);
SHA1_Final (checksum, &ctx);
rawdata_to_hex (checksum, checksum_str, 20);
if (write_block (dst_repo->store_id, checksum_str, dst_repo->version, src_dec_out, src_dec_out_len) < 0) {
goto out;
}
ret = g_strdup (checksum_str);
} else if (!src_crypt && dst_crypt) {
// Destination repo is encrypted.
char *dst_enc_buf = NULL;
int dst_enc_len = -1;
int rc = seafile_encrypt (&dst_enc_buf, &dst_enc_len,
buf, block_size, dst_crypt);
if (rc != 0) {
seaf_warning ("Failed to encrypt block for repo %s.\n", dst_repo->id);
goto out;
}
SHA1_Init (&ctx);
SHA1_Update (&ctx, dst_enc_buf, dst_enc_len);
SHA1_Final (checksum, &ctx);
rawdata_to_hex (checksum, checksum_str, 20);
if (write_block (dst_repo->store_id, checksum_str, dst_repo->version, dst_enc_buf, dst_enc_len) < 0) {
g_free (dst_enc_buf);
goto out;
}
g_free (dst_enc_buf);
ret = g_strdup (checksum_str);
} else if (!src_crypt && !dst_crypt) {
// Both source and destination repos are not encrypted reops.
if (write_block (dst_repo->store_id, block_id, dst_repo->version, buf, block_size) < 0) {
goto out;
}
ret = g_strdup (checksum_str);
}
out:
g_free (buf);
g_free (src_dec_out);
if (handle) {
seaf_block_manager_close_block (mgr, handle);
seaf_block_manager_block_handle_free (mgr, handle);
}
if (bmd)
g_free (bmd);
return ret;
}
static char *
copy_seafile (SeafRepo *src_repo, SeafRepo *dst_repo,
SeafileCrypt *src_crypt, SeafileCrypt *dst_crypt,
const char *file_id, CopyTask *task, guint64 *size)
{
Seafile *file;
@ -1913,16 +2099,6 @@ copy_seafile (SeafRepo *src_repo, SeafRepo *dst_repo, const char *file_id,
/* We may be copying from v0 repo to v1 repo or vise versa. */
file->version = seafile_version_from_repo_version(dst_repo->version);
if (seafile_save (seaf->fs_mgr,
dst_repo->store_id,
dst_repo->version,
file) < 0) {
seaf_warning ("Failed to copy file object %s from repo %s to %s.\n",
file_id, src_repo->id, dst_repo->id);
seafile_unref (file);
return NULL;
}
int i;
char *block_id;
for (i = 0; i < file->n_blocks; ++i) {
@ -1931,19 +2107,41 @@ copy_seafile (SeafRepo *src_repo, SeafRepo *dst_repo, const char *file_id,
seafile_unref (file);
return NULL;
}
block_id = file->blk_sha1s[i];
if (seaf_block_manager_copy_block (seaf->block_mgr,
src_repo->store_id, src_repo->version,
dst_repo->store_id, dst_repo->version,
block_id) < 0) {
seaf_warning ("Failed to copy block %s from repo %s to %s.\n",
block_id, src_repo->id, dst_repo->id);
seafile_unref (file);
return NULL;
if (src_crypt != NULL || dst_crypt != NULL) {
char *new_block_id = copy_block_between_enc_repo (src_repo, dst_repo, src_crypt, dst_crypt, block_id);
if (new_block_id == NULL) {
seaf_warning ("Failed to copy block %s from repo %s to %s.\n",
block_id, src_repo->id, dst_repo->id);
seafile_unref (file);
return NULL;
}
g_free (file->blk_sha1s[i]);
file->blk_sha1s[i] = new_block_id;
} else {
if (seaf_block_manager_copy_block (seaf->block_mgr,
src_repo->store_id, src_repo->version,
dst_repo->store_id, dst_repo->version,
block_id) < 0) {
seaf_warning ("Failed to copy block %s from repo %s to %s.\n",
block_id, src_repo->id, dst_repo->id);
seafile_unref (file);
return NULL;
}
}
}
// Save fs after copy blocks, block_id may be changed when copy between encrypted repos.
if (seafile_save (seaf->fs_mgr,
dst_repo->store_id,
dst_repo->version,
file) < 0) {
seaf_warning ("Failed to copy file object %s from repo %s to %s.\n",
file_id, src_repo->id, dst_repo->id);
seafile_unref (file);
return NULL;
}
if (task)
++(task->done);
@ -1956,11 +2154,12 @@ copy_seafile (SeafRepo *src_repo, SeafRepo *dst_repo, const char *file_id,
static char *
copy_recursive (SeafRepo *src_repo, SeafRepo *dst_repo,
SeafileCrypt *src_crypt, SeafileCrypt *dst_crypt,
const char *obj_id, guint32 mode, const char *modifier,
CopyTask *task, guint64 *size)
{
if (S_ISREG(mode)) {
return copy_seafile (src_repo, dst_repo, obj_id, task, size);
return copy_seafile (src_repo, dst_repo, src_crypt, dst_crypt, obj_id, task, size);
} else if (S_ISDIR(mode)) {
SeafDir *src_dir = NULL, *dst_dir = NULL;
GList *dst_ents = NULL, *ptr;
@ -1981,7 +2180,7 @@ copy_recursive (SeafRepo *src_repo, SeafRepo *dst_repo,
dent = ptr->data;
guint64 new_size = 0;
new_id = copy_recursive (src_repo, dst_repo,
new_id = copy_recursive (src_repo, dst_repo, src_crypt, dst_crypt,
dent->id, dent->mode, modifier, task, &new_size);
if (!new_id) {
seaf_dir_free (src_dir);
@ -2058,6 +2257,37 @@ set_failed_reason (char **failed_reason, char *err_str)
*failed_reason = g_strdup (err_str);
}
static SeafileCrypt *
get_crypt_by_repo (SeafRepo *repo, const char *user)
{
char *key_hex, *iv_hex;
unsigned char enc_key[32], enc_iv[16];
SeafileCryptKey *key = NULL;
SeafileCrypt *crypt = NULL;
key = seaf_passwd_manager_get_decrypt_key (seaf->passwd_mgr,
repo->id, user);
if (!key) {
return NULL;
}
g_object_get (key,
"key", &key_hex,
"iv", &iv_hex,
NULL);
if (repo->enc_version == 1)
hex_to_rawdata (key_hex, enc_key, 16);
else
hex_to_rawdata (key_hex, enc_key, 32);
hex_to_rawdata (iv_hex, enc_iv, 16);
crypt = seafile_crypt_new (repo->enc_version, enc_key, enc_iv);
g_free (key_hex);
g_free (iv_hex);
g_object_unref (key);
return crypt;
}
static int
cross_repo_copy (const char *src_repo_id,
const char *src_path,
@ -2076,11 +2306,13 @@ cross_repo_copy (const char *src_repo_id,
char *new_id = NULL;
guint64 new_size = 0;
int ret = 0, i = 0;
int file_num = 1;
int file_num = 0;
GHashTable *dirent_hash = NULL;
gint64 total_size_all = 0;
char *err_str = COPY_ERR_INTERNAL;
int check_quota_ret;
SeafileCrypt *src_crypt = NULL;
SeafileCrypt *dst_crypt = NULL;
src_repo = seaf_repo_manager_get_repo (seaf->repo_mgr, src_repo_id);
if (!src_repo) {
@ -2090,6 +2322,16 @@ cross_repo_copy (const char *src_repo_id,
goto out;
}
if (src_repo->encrypted) {
src_crypt = get_crypt_by_repo (src_repo, modifier);
if (!src_crypt) {
err_str = COPY_ERR_INTERNAL;
ret = -1;
seaf_warning ("The source repo is encrypted. Please provide password to view it.\n");
goto out;
}
}
dst_repo = seaf_repo_manager_get_repo (seaf->repo_mgr, dst_repo_id);
if (!dst_repo) {
err_str = COPY_ERR_INTERNAL;
@ -2098,6 +2340,16 @@ cross_repo_copy (const char *src_repo_id,
goto out;
}
if (dst_repo->encrypted) {
dst_crypt = get_crypt_by_repo (dst_repo, modifier);
if (!dst_crypt) {
err_str = COPY_ERR_INTERNAL;
ret = -1;
seaf_warning ("The destination repo is encrypted. Please provide password to view it.\n");
goto out;
}
}
src_names = json_to_file_list (src_filename);
dst_names = json_to_file_list (dst_filename);
file_num = g_list_length (src_names);
@ -2171,7 +2423,7 @@ cross_repo_copy (const char *src_repo_id,
/* do copy */
for (ptr = dst_names; ptr; ptr = ptr->next) {
name = ptr->data;
new_id = copy_recursive (src_repo, dst_repo,
new_id = copy_recursive (src_repo, dst_repo, src_crypt, dst_crypt,
src_dents[i]->id, src_dents[i]->mode, modifier, task,
&new_size);
if (!new_id) {
@ -2209,6 +2461,8 @@ out:
seaf_repo_unref (src_repo);
if (dst_repo)
seaf_repo_unref (dst_repo);
g_free (src_crypt);
g_free (dst_crypt);
if (dirent_hash)
g_hash_table_unref(dirent_hash);
g_free(src_dents);
@ -2484,14 +2738,6 @@ seaf_repo_manager_copy_multiple_files (SeafRepoManager *mgr,
if (strcmp(src_repo_id, dst_repo_id) != 0) {
GET_REPO_OR_FAIL(dst_repo, dst_repo_id);
if (src_repo->encrypted || dst_repo->encrypted) {
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_ARGS,
"Can't copy files between encrypted repo(s)");
ret = -1;
goto out;
}
} else {
seaf_repo_ref (src_repo);
dst_repo = src_repo;
@ -2761,11 +3007,13 @@ cross_repo_move (const char *src_repo_id,
char *new_id = NULL;
guint64 new_size = 0;
int ret = 0, i = 0;
int file_num = 1;
int file_num = 0;
GHashTable *dirent_hash = NULL;
gint64 total_size_all = 0;
char *err_str = COPY_ERR_INTERNAL;
int check_quota_ret;
SeafileCrypt *src_crypt = NULL;
SeafileCrypt *dst_crypt = NULL;
src_repo = seaf_repo_manager_get_repo (seaf->repo_mgr, src_repo_id);
if (!src_repo) {
@ -2775,6 +3023,16 @@ cross_repo_move (const char *src_repo_id,
goto out;
}
if (src_repo->encrypted) {
src_crypt = get_crypt_by_repo (src_repo, modifier);
if (!src_crypt) {
err_str = COPY_ERR_INTERNAL;
ret = -1;
seaf_warning ("The source repo is encrypted. Please provide password to view it.\n");
goto out;
}
}
dst_repo = seaf_repo_manager_get_repo (seaf->repo_mgr, dst_repo_id);
if (!dst_repo) {
err_str = COPY_ERR_INTERNAL;
@ -2783,6 +3041,16 @@ cross_repo_move (const char *src_repo_id,
goto out;
}
if (dst_repo->encrypted) {
dst_crypt = get_crypt_by_repo (dst_repo, modifier);
if (!dst_crypt) {
err_str = COPY_ERR_INTERNAL;
ret = -1;
seaf_warning ("The destination repo is encrypted. Please provide password to view it.\n");
goto out;
}
}
src_names = json_to_file_list (src_filename);
dst_names = json_to_file_list (dst_filename);
@ -2857,7 +3125,7 @@ cross_repo_move (const char *src_repo_id,
/* do copy */
for (ptr = dst_names; ptr; ptr = ptr->next) {
name = ptr->data;
new_id = copy_recursive (src_repo, dst_repo,
new_id = copy_recursive (src_repo, dst_repo, src_crypt, dst_crypt,
src_dents[i]->id, src_dents[i]->mode, modifier, task,
&new_size);
if (!new_id) {
@ -2904,6 +3172,8 @@ out:
seaf_repo_unref (src_repo);
if (dst_repo)
seaf_repo_unref (dst_repo);
g_free (src_crypt);
g_free (dst_crypt);
if (dirent_hash)
g_hash_table_unref(dirent_hash);
g_free (src_dents);
@ -3142,14 +3412,6 @@ seaf_repo_manager_move_multiple_files (SeafRepoManager *mgr,
if (strcmp(src_repo_id, dst_repo_id) != 0) {
GET_REPO_OR_FAIL(dst_repo, dst_repo_id);
if (src_repo->encrypted || dst_repo->encrypted) {
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_ARGS,
"Can't copy files between encrypted repo(s)");
ret = -1;
goto out;
}
} else {
seaf_repo_ref (src_repo);
dst_repo = src_repo;