static void hash_password_pbkdf2_sha256 (const char *passwd, int iterations, char **db_passwd) { guint8 sha[SHA256_DIGEST_LENGTH]; guint8 salt[SHA256_DIGEST_LENGTH]; char hashed_passwd[SHA256_DIGEST_LENGTH*2+1]; char salt_str[SHA256_DIGEST_LENGTH*2+1]; if (!RAND_bytes (salt, sizeof(salt))) { ccnet_warning ("Failed to generate salt " "with RAND_bytes(), use RAND_pseudo_bytes().\n"); RAND_pseudo_bytes (salt, sizeof(salt)); } PKCS5_PBKDF2_HMAC (passwd, strlen(passwd), salt, sizeof(salt), iterations, EVP_sha256(), sizeof(sha), sha); rawdata_to_hex (sha, hashed_passwd, SHA256_DIGEST_LENGTH); rawdata_to_hex (salt, salt_str, SHA256_DIGEST_LENGTH); /* Encode password hash related information into one string, similar to Django. */ GString *buf = g_string_new (NULL); g_string_printf (buf, "PBKDF2SHA256$%d$%s$%s", iterations, salt_str, hashed_passwd); *db_passwd = g_string_free (buf, FALSE); }
SeafileCryptKey * seaf_passwd_manager_get_decrypt_key (SeafPasswdManager *mgr, const char *repo_id, const char *user) { GString *hash_key; DecryptKey *crypt_key; SeafileCryptKey *ret; char key_hex[65], iv_hex[65]; hash_key = g_string_new (NULL); g_string_printf (hash_key, "%s.%s", repo_id, user); /* g_debug ("[passwd mgr] get passwd for %s.\n", hash_key->str); */ crypt_key = g_hash_table_lookup (mgr->priv->decrypt_keys, hash_key->str); if (!crypt_key) { g_string_free (hash_key, TRUE); return NULL; } if (crypt_key->enc_version == 2) { rawdata_to_hex (crypt_key->key, key_hex, 32); rawdata_to_hex (crypt_key->iv, iv_hex, 16); } else if (crypt_key->enc_version == 1) { rawdata_to_hex (crypt_key->key, key_hex, 16); rawdata_to_hex (crypt_key->iv, iv_hex, 16); } ret = seafile_crypt_key_new (); g_object_set (ret, "key", key_hex, "iv", iv_hex, NULL); g_string_free (hash_key, TRUE); return ret; }
int compare_file_content (const char *path, struct stat *st, const unsigned char *ce_sha1, SeafileCrypt *crypt) { CDCFileDescriptor cdc; unsigned char sha1[20]; memset (&cdc, 0, sizeof(cdc)); cdc.block_sz = calculate_chunk_size (st->st_size); cdc.block_min_sz = cdc.block_sz >> 2; cdc.block_max_sz = cdc.block_sz << 2; cdc.write_block = seafile_write_chunk; if (filename_chunk_cdc (path, &cdc, crypt, FALSE) < 0) { g_warning ("Failed to chunk file.\n"); return -1; } memcpy (sha1, cdc.file_sum, 20); char id1[41], id2[41]; rawdata_to_hex (sha1, id1, 20); rawdata_to_hex (ce_sha1, id2, 20); printf ("id1: %s, id2: %s.\n", id1, id2); return hashcmp (sha1, ce_sha1); }
int seafile_verify_repo_passwd (const char *repo_id, const char *passwd, const char *magic, int version) { GString *buf = g_string_new (NULL); unsigned char key[32], iv[16]; char hex[65]; if (version != 1 && version != 2) { seaf_warning ("Unsupported enc_version %d.\n", version); return -1; } /* Recompute the magic and compare it with the one comes with the repo. */ g_string_append_printf (buf, "%s%s", repo_id, passwd); seafile_derive_key (buf->str, buf->len, version, key, iv); g_string_free (buf, TRUE); if (version == 2) rawdata_to_hex (key, hex, 32); else rawdata_to_hex (key, hex, 16); if (g_strcmp0 (hex, magic) == 0) return 0; else return -1; }
int test_write_chunk (const char *repo_id, int version, CDCDescriptor *chunk_descr, struct SeafileCrypt *crypt, uint8_t *checksum, gboolean write_data) { char filename[NAME_MAX_SZ]; char chksum_str[CHECKSUM_LENGTH *2 + 1]; int fd_chunk, ret; SHA_CTX ctx; SHA1_Init (&ctx); SHA1_Update (&ctx, chunk_descr->block_buf, chunk_descr->len); SHA1_Final (checksum, &ctx); rawdata_to_hex (chunk_descr->checksum, chksum_str, CHECKSUM_LENGTH); snprintf (filename, NAME_MAX_SZ, "%s/%s", dest_dir, chksum_str); fd_chunk = g_open (filename, O_WRONLY | O_CREAT | O_BINARY, 0644); if (fd_chunk < 0) return -1; ret = write (fd_chunk, chunk_descr->block_buf, chunk_descr->len); return ret; }
void seafile_generate_random_key (const char *passwd, char *random_key) { SeafileCrypt *crypt; unsigned char secret_key[32], *rand_key; int outlen; unsigned char key[32], iv[16]; if (RAND_bytes (secret_key, sizeof(secret_key)) != 1) { seaf_warning ("Failed to generate secret key for repo encryption " "with RAND_bytes(), use RAND_pseudo_bytes().\n"); RAND_pseudo_bytes (secret_key, sizeof(secret_key)); } seafile_derive_key (passwd, strlen(passwd), 2, key, iv); crypt = seafile_crypt_new (2, key, iv); seafile_encrypt ((char **)&rand_key, &outlen, (char *)secret_key, sizeof(secret_key), crypt); rawdata_to_hex (rand_key, random_key, 48); g_free (crypt); g_free (rand_key); }
static gboolean validate_passwd_pbkdf2_sha256 (const char *passwd, const char *db_passwd) { char **tokens; char *salt_str, *hash; int iter; guint8 sha[SHA256_DIGEST_LENGTH]; guint8 salt[SHA256_DIGEST_LENGTH]; char hashed_passwd[SHA256_DIGEST_LENGTH*2+1]; tokens = g_strsplit (db_passwd, "$", -1); if (!tokens || g_strv_length (tokens) != 4) { ccnet_warning ("Invalide db passwd format %s.\n", db_passwd); return FALSE; } iter = atoi (tokens[1]); salt_str = tokens[2]; hash = tokens[3]; hex_to_rawdata (salt_str, salt, SHA256_DIGEST_LENGTH); PKCS5_PBKDF2_HMAC (passwd, strlen(passwd), salt, sizeof(salt), iter, EVP_sha256(), sizeof(sha), sha); rawdata_to_hex (sha, hashed_passwd, SHA256_DIGEST_LENGTH); gboolean ret = (strcmp (hash, hashed_passwd) == 0); g_strfreev (tokens); return ret; }
char *write_tree_from_memory(struct merge_options *o) { struct cache_tree *it; char root_id[41]; if (unmerged_index(o->index)) { int i; fprintf(stderr, "BUG: There are unmerged index entries:\n"); for (i = 0; i < o->index->cache_nr; i++) { struct cache_entry *ce = o->index->cache[i]; if (ce_stage(ce)) fprintf(stderr, "BUG: %d %.*s", ce_stage(ce), (int)ce_namelen(ce), ce->name); } g_assert(0); } /* if (!active_cache_tree) */ it = cache_tree(); if (cache_tree_update(it, o->index->cache, o->index->cache_nr, 0, 0, commit_trees_cb) < 0) { g_warning("error building trees"); cache_tree_free (&it); return NULL; } rawdata_to_hex(it->sha1, root_id, 20); cache_tree_free (&it); return g_strdup(root_id); }
static int print_index (SeafRepo *repo) { char *index_file; struct index_state istate; index_file = g_build_path (PATH_SEPERATOR, SEAF_DIR, "index", repo->id, NULL); memset (&istate, 0, sizeof(istate)); if (read_index_from (&istate, index_file) < 0) { fprintf (stderr, "Corrupt index file %s\n", index_file); return -1; } printf ("Index timestamp: %d\n", istate.timestamp.sec); int i; struct cache_entry *ce; char id[41]; printf ("Totally %u entries in index.\n", istate.cache_nr); for (i = 0; i < istate.cache_nr; ++i) { ce = istate.cache[i]; rawdata_to_hex (ce->sha1, id, 20); printf ("%s\t%s\t%o\t%d\t%d\n", ce->name, id, ce->ce_mode, ce->ce_ctime.sec, ce->ce_mtime.sec); } return 0; }
static void hash_password (const char *passwd, char *hashed_passwd) { unsigned char sha1[20]; SHA_CTX s; SHA1_Init (&s); SHA1_Update (&s, passwd, strlen(passwd)); SHA1_Final (sha1, &s); rawdata_to_hex (sha1, hashed_passwd, 20); }
static void hash_password_salted (const char *passwd, char *hashed_passwd) { unsigned char sha[SHA256_DIGEST_LENGTH]; SHA256_CTX s; SHA256_Init (&s); SHA256_Update (&s, passwd, strlen(passwd)); SHA256_Update (&s, salt, sizeof(salt)); SHA256_Final (sha, &s); rawdata_to_hex (sha, hashed_passwd, SHA256_DIGEST_LENGTH); }
int compare_file_content (const char *path, SeafStat *st, const unsigned char *ce_sha1, SeafileCrypt *crypt, int repo_version) { CDCFileDescriptor cdc; unsigned char sha1[20]; if (st->st_size == 0) { memset (sha1, 0, 20); } else { memset (&cdc, 0, sizeof(cdc)); cdc.block_sz = calculate_chunk_size (st->st_size); cdc.block_min_sz = cdc.block_sz >> 2; cdc.block_max_sz = cdc.block_sz << 2; cdc.write_block = seafile_write_chunk; if (filename_chunk_cdc (path, &cdc, crypt, FALSE) < 0) { g_warning ("Failed to chunk file.\n"); return -1; } if (repo_version > 0) seaf_fs_manager_calculate_seafile_id_json (repo_version, &cdc, sha1); else memcpy (sha1, cdc.file_sum, 20); if (cdc.blk_sha1s) free (cdc.blk_sha1s); } #if 0 char id1[41], id2[41]; rawdata_to_hex (sha1, id1, 20); rawdata_to_hex (ce_sha1, id2, 20); seaf_debug ("id1: %s, id2: %s.\n", id1, id2); #endif return hashcmp (sha1, ce_sha1); }
static void print_index (struct index_state *istate) { printf ("Totally %u entries in index.\n", istate->cache_nr); int i; char id[41]; for (i = 0; i < istate->cache_nr; ++i) { struct cache_entry *ce = istate->cache[i]; rawdata_to_hex (ce->sha1, id, 20); printf ("%s\t%s\t%o\t%d\t%d\t%d\n", ce->name, id, ce->ce_mode, ce_stage(ce), ce->ce_ctime.sec, ce->ce_mtime.sec); } }
/* 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 (CURRENT_ENC_VERSION, 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; }
int test_chunks (CDCFileDescriptor *file_descriptor) { struct stat sb; char chksum_str[CHECKSUM_LENGTH *2 + 1]; char filename[NAME_MAX_SZ]; uint8_t *ptr = file_descriptor->blk_sha1s; int i = 0; int max_sz = -1, min_sz = INT_MAX, total_sz = 0; printf ("%d chunks.\n", file_descriptor->block_nr); while (i < file_descriptor->block_nr) { rawdata_to_hex (ptr, chksum_str, CHECKSUM_LENGTH); snprintf (filename, NAME_MAX_SZ, "%s/%s", dest_dir, chksum_str); if (g_stat (filename, &sb) < 0) { perror ("stat"); return -1; } if (sb.st_size < min_sz) min_sz = sb.st_size; if (sb.st_size > max_sz) max_sz = sb.st_size; total_sz += sb.st_size; printf ("%s: %u bytes\n", chksum_str, (uint32_t)sb.st_size); if (sb.st_size > file_descriptor->block_max_sz) { fprintf (stderr, "chunk size too large: %s.\n", chksum_str); return -1; } if (sb.st_size < file_descriptor->block_min_sz && i != file_descriptor->block_nr - 1) { fprintf (stderr, "chunk size too small: %s.\n", chksum_str); return -1; } ptr += CHECKSUM_LENGTH; ++i; } printf ("max size: %d\n", max_sz); printf ("min size: %d\n", min_sz); printf ("avg size: %d\n", total_sz/file_descriptor->block_nr); return 0; }
gboolean seaf_block_manager_verify_block (SeafBlockManager *mgr, const char *store_id, int version, const char *block_id, gboolean *io_error) { BlockHandle *h; char buf[10240]; int n; SHA_CTX ctx; guint8 sha1[20]; char check_id[41]; h = seaf_block_manager_open_block (mgr, store_id, version, block_id, BLOCK_READ); if (!h) { seaf_warning ("Failed to open block %.8s.\n", block_id); *io_error = TRUE; return FALSE; } SHA1_Init (&ctx); while (1) { n = seaf_block_manager_read_block (mgr, h, buf, sizeof(buf)); if (n < 0) { seaf_warning ("Failed to read block %.8s.\n", block_id); *io_error = TRUE; return FALSE; } if (n == 0) break; SHA1_Update (&ctx, buf, n); } seaf_block_manager_close_block (mgr, h); seaf_block_manager_block_handle_free (mgr, h); SHA1_Final (sha1, &ctx); rawdata_to_hex (sha1, check_id, 20); if (strcmp (check_id, block_id) == 0) return TRUE; else return FALSE; }
static int default_write_chunk (CDCDescriptor *chunk_descr) { char filename[NAME_MAX_SZ]; char chksum_str[CHECKSUM_LENGTH *2 + 1]; int fd_chunk, ret; memset(chksum_str, 0, sizeof(chksum_str)); rawdata_to_hex (chunk_descr->checksum, chksum_str, CHECKSUM_LENGTH); snprintf (filename, NAME_MAX_SZ, "./%s", chksum_str); fd_chunk = g_open (filename, O_RDWR | O_CREAT | O_BINARY, 0644); if (fd_chunk < 0) return -1; ret = writen (fd_chunk, chunk_descr->block_buf, chunk_descr->len); close (fd_chunk); return ret; }
void seafile_generate_magic (int version, const char *repo_id, const char *passwd, char *magic) { GString *buf = g_string_new (NULL); unsigned char key[32], iv[16]; /* Compute a "magic" string from repo_id and passwd. * This is used to verify the password given by user before decrypting * data. */ g_string_append_printf (buf, "%s%s", repo_id, passwd); seafile_derive_key (buf->str, buf->len, version, key, iv); g_string_free (buf, TRUE); rawdata_to_hex (key, magic, 32); }
/* 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 v2] peer or peer session key not exist\n"); goto out; } seafile_generate_enc_key (peer->session_key, strlen(peer->session_key), CURRENT_ENC_VERSION, key, iv); crypt = seafile_crypt_new (CURRENT_ENC_VERSION, 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 v2] 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; }
void fill_seafile_blocks (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, file_id); if (!seafile) { g_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); }
int seafile_update_random_key (const char *old_passwd, const char *old_random_key, const char *new_passwd, char *new_random_key) { unsigned char key[32], iv[16]; unsigned char random_key_raw[48], *secret_key, *new_random_key_raw; int secret_key_len, random_key_len; SeafileCrypt *crypt; /* First, use old_passwd to decrypt secret key from old_random_key. */ seafile_derive_key (old_passwd, strlen(old_passwd), 2, key, iv); hex_to_rawdata (old_random_key, random_key_raw, 48); crypt = seafile_crypt_new (2, key, iv); if (seafile_decrypt ((char **)&secret_key, &secret_key_len, (char *)random_key_raw, 48, crypt) < 0) { seaf_warning ("Failed to decrypt random key.\n"); g_free (crypt); return -1; } g_free (crypt); /* Second, use new_passwd to encrypt secret key. */ seafile_derive_key (new_passwd, strlen(new_passwd), 2, key, iv); crypt = seafile_crypt_new (2, key, iv); seafile_encrypt ((char **)&new_random_key_raw, &random_key_len, (char *)secret_key, secret_key_len, crypt); rawdata_to_hex (new_random_key_raw, new_random_key, 48); g_free (secret_key); g_free (new_random_key_raw); g_free (crypt); return 0; }
int commit_trees_cb (struct cache_tree *it, struct cache_entry **cache, int entries, const char *base, int baselen) { SeafDir *seaf_dir; GList *dirents = NULL; int i; for (i = 0; i < entries; i++) { SeafDirent *seaf_dent; char *dirname; struct cache_entry *ce = cache[i]; struct cache_tree_sub *sub; const char *path, *slash; int pathlen, entlen; const unsigned char *sha1; char hex[41]; unsigned mode; if (ce->ce_flags & CE_REMOVE) continue; /* entry being removed */ path = ce->name; pathlen = ce_namelen(ce); if (pathlen <= baselen || memcmp(base, path, baselen)) break; /* at the end of this level */ slash = strchr(path + baselen, '/'); if (slash) { entlen = slash - (path + baselen); sub = cache_tree_find_subtree(it, path + baselen, entlen, 0); g_assert (sub != NULL); /* Skip cache entries in the sub level. */ i += sub->cache_tree->entry_count - 1; sha1 = sub->cache_tree->sha1; mode = S_IFDIR; dirname = g_strndup(path + baselen, entlen); rawdata_to_hex (sha1, hex, 20); seaf_dent = seaf_dirent_new (hex, mode, dirname); g_free(dirname); dirents = g_list_prepend (dirents, seaf_dent); } else { sha1 = ce->sha1; mode = ce->ce_mode; entlen = pathlen - baselen; dirname = g_strndup(path + baselen, entlen); rawdata_to_hex (sha1, hex, 20); seaf_dent = seaf_dirent_new (hex, mode, dirname); g_free(dirname); dirents = g_list_prepend (dirents, seaf_dent); } #if DEBUG fprintf(stderr, "cache-tree update-one %o %.*s\n", mode, entlen, path + baselen); #endif } /* Sort dirents in descending order. */ dirents = g_list_sort (dirents, compare_dirents); seaf_dir = seaf_dir_new (NULL, dirents, 0); hex_to_rawdata (seaf_dir->dir_id, it->sha1, 20); seaf_dir_save (seaf->fs_mgr, seaf_dir); #if DEBUG for (p = dirents; p; p = p->next) { SeafDirent *tmp = (SeafDirent *)p->data; fprintf(stderr, "dump dirent name %s id %s\n", tmp->name, tmp->id); } #endif seaf_dir_free (seaf_dir); return 0; }
static int checkout_entry (struct cache_entry *ce, struct unpack_trees_options *o, gboolean recover_merge, const char *conflict_suffix) { int base_len = strlen(o->base); int len = ce_namelen(ce); int full_len; char path[PATH_MAX]; int offset; struct stat st; char file_id[41]; if (!len) { g_warning ("entry name should not be empty.\n"); return -1; } snprintf (path, PATH_MAX, "%s/", o->base); /* first create all leading directories. */ full_len = base_len + len + 1; offset = base_len + 1; while (offset < full_len) { do { path[offset] = ce->name[offset-base_len-1]; offset++; } while (offset < full_len && ce->name[offset-base_len-1] != '/'); if (offset >= full_len) break; path[offset] = 0; if (g_lstat (path, &st) == 0 && S_ISDIR(st.st_mode)) continue; if (ccnet_mkdir (path, 0777) < 0) { g_warning ("Failed to create directory %s.\n", path); return -1; } } path[offset] = 0; 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 (g_lstat (path, &st) == 0 && S_ISDIR(st.st_mode)) { if (g_rmdir (path) < 0) { g_warning ("Failed to remove dir %s: %s\n", path, strerror(errno)); /* Don't quit since we can handle conflict later. */ } } } else { /* For simplicity, we just don't checkout the empty dir if there is * already a file with the same name in the worktree. * This implies, you can't remove a file then create an empty directory * with the same name. But it's a rare requirement. */ if (g_mkdir (path, 0777) < 0) { g_warning ("Failed to create empty dir %s.\n", path); } return 0; } if (!o->reset && g_lstat (path, &st) == 0 && S_ISREG(st.st_mode) && (ce->ce_ctime.sec != st.st_ctime || ce->ce_mtime.sec != 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) != 0) { g_warning ("File %s is changed. Skip checking out.\n", path); return -1; } /* 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. */ rawdata_to_hex (ce->sha1, file_id, 20); if (seaf_fs_manager_checkout_file (seaf->fs_mgr, file_id, path, ce->ce_mode, o->crypt, conflict_suffix) < 0) { g_warning ("Failed to checkout file %s.\n", path); return -1; } update_cache: /* finally fill cache_entry info */ g_lstat (path, &st); fill_stat_cache_info (ce, &st); return 0; }
int commit_trees_cb (const char *repo_id, int version, const char *worktree, struct cache_tree *it, struct cache_entry **cache, int entries, const char *base, int baselen) { SeafDir *seaf_dir; GList *dirents = NULL, *ptr; int i; for (i = 0; i < entries; i++) { SeafDirent *seaf_dent; char *name; struct cache_entry *ce = cache[i]; struct cache_tree_sub *sub; const char *path, *slash; int pathlen, entlen; const unsigned char *sha1; char hex[41]; unsigned mode; guint64 mtime; gint64 size; char *modifier; if (ce->ce_flags & CE_REMOVE) continue; /* entry being removed */ path = ce->name; pathlen = ce_namelen(ce); if (pathlen <= baselen || memcmp(base, path, baselen)) break; /* at the end of this level */ slash = strchr(path + baselen, '/'); if (slash) { entlen = slash - (path + baselen); sub = cache_tree_find_subtree(it, path + baselen, entlen, 0); g_return_val_if_fail (sub != NULL, -1); /* Skip cache entries in the sub level. */ i += sub->cache_tree->entry_count - 1; sha1 = sub->cache_tree->sha1; mtime = sub->cache_tree->mtime; mode = S_IFDIR; name = g_strndup(path + baselen, entlen); rawdata_to_hex (sha1, hex, 20); seaf_dent = seaf_dirent_new (dir_version_from_repo_version(version), hex, mode, name, mtime, NULL, -1); g_free(name); dirents = g_list_prepend (dirents, seaf_dent); } else { sha1 = ce->sha1; mode = ce->ce_mode; mtime = ce->ce_mtime.sec; size = ce->ce_size; modifier = ce->modifier; entlen = pathlen - baselen; name = g_strndup(path + baselen, entlen); rawdata_to_hex (sha1, hex, 20); if (version > 0) { seaf_dent = seaf_dirent_new (dir_version_from_repo_version(version), hex, mode, name, mtime, modifier, size); } else { seaf_dent = seaf_dirent_new (0, hex, mode, name, 0, NULL, -1); } g_free(name); dirents = g_list_prepend (dirents, seaf_dent); } #if DEBUG fprintf(stderr, "cache-tree update-one %o %.*s\n", mode, entlen, path + baselen); #endif } /* Sort dirents in descending order. */ dirents = g_list_sort (dirents, compare_dirents); seaf_dir = seaf_dir_new (NULL, dirents, dir_version_from_repo_version(version)); hex_to_rawdata (seaf_dir->dir_id, it->sha1, 20); /* Dir's mtime is the latest of all dir entires. */ guint64 dir_mtime = 0; SeafDirent *dent; for (ptr = dirents; ptr; ptr = ptr->next) { dent = ptr->data; if (dent->mtime > dir_mtime) dir_mtime = dent->mtime; } it->mtime = dir_mtime; if (!seaf_fs_manager_object_exists (seaf->fs_mgr, repo_id, version, seaf_dir->dir_id)) seaf_dir_save (seaf->fs_mgr, repo_id, version, seaf_dir); #if DEBUG for (p = dirents; p; p = p->next) { SeafDirent *tmp = (SeafDirent *)p->data; fprintf(stderr, "dump dirent name %s id %s\n", tmp->name, tmp->id); } #endif seaf_dir_free (seaf_dir); return 0; }
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); #ifndef __linux__ 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 (g_rmdir (path) < 0) { g_warning ("Failed to remove dir %s: %s\n", path, strerror(errno)); /* Don't quit since we can handle conflict later. */ } } } else { if (g_mkdir (path, 0777) < 0) { g_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) { g_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) { g_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) < 0) { g_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; }
/* * If the missing virtual repo is renamed, update database entry; * otherwise delete the virtual repo. */ static void handle_missing_virtual_repo (SeafRepoManager *mgr, SeafRepo *repo, SeafCommit *head, SeafVirtRepo *vinfo) { SeafCommit *parent = NULL; char *old_dir_id = NULL; GList *diff_res = NULL, *ptr; DiffEntry *de; parent = seaf_commit_manager_get_commit (seaf->commit_mgr, head->repo_id, head->version, head->parent_id); if (!parent) { seaf_warning ("Failed to find commit %s:%s.\n", head->repo_id, head->parent_id); return; } int rc = diff_commits (parent, head, &diff_res, TRUE); if (rc < 0) { seaf_warning ("Failed to diff commit %s to %s.\n", parent->commit_id, head->commit_id); seaf_commit_unref (parent); return; } char *path = vinfo->path, *sub_path, *p, *par_path; gboolean is_renamed = FALSE; p = &path[strlen(path)]; par_path = g_strdup(path); sub_path = NULL; while (1) { GError *error = NULL; old_dir_id = seaf_fs_manager_get_seafdir_id_by_path (seaf->fs_mgr, repo->store_id, repo->version, parent->root_id, par_path, &error); if (!old_dir_id) { if (error && error->code == SEAF_ERR_PATH_NO_EXIST) { seaf_warning ("Failed to find %s under commit %s in repo %s.\n", par_path, parent->commit_id, repo->store_id); seaf_debug ("Delete virtual repo %.10s.\n", vinfo->repo_id); seaf_repo_manager_del_virtual_repo (mgr, vinfo->repo_id); g_clear_error (&error); } goto out; } char de_id[41]; char *new_path; for (ptr = diff_res; ptr; ptr = ptr->next) { de = ptr->data; if (de->status == DIFF_STATUS_DIR_RENAMED) { rawdata_to_hex (de->sha1, de_id, 20); if (strcmp (de_id, old_dir_id) == 0) { if (sub_path != NULL) new_path = g_strconcat ("/", de->new_name, "/", sub_path, NULL); else new_path = g_strconcat ("/", de->new_name, NULL); seaf_debug ("Updating path of virtual repo %s to %s.\n", vinfo->repo_id, new_path); set_virtual_repo_base_commit_path (vinfo->repo_id, head->commit_id, new_path); is_renamed = TRUE; break; } } } g_free (old_dir_id); if (is_renamed) break; while (--p != path && *p != '/'); if (p == path) break; g_free (par_path); g_free (sub_path); par_path = g_strndup (path, p - path); sub_path = g_strdup (p + 1); } if (!is_renamed) { seaf_debug ("Delete virtual repo %.10s.\n", vinfo->repo_id); seaf_repo_manager_del_virtual_repo (mgr, vinfo->repo_id); } out: g_free (par_path); g_free (sub_path); for (ptr = diff_res; ptr; ptr = ptr->next) diff_entry_free ((DiffEntry *)ptr->data); g_list_free (diff_res); seaf_commit_unref (parent); }
static int update_file_flags(struct merge_options *o, const unsigned char *sha, unsigned mode, const char *path, int update_cache, int update_wd) { char *real_path; char file_id[41]; int clean = 1; if (update_wd && o->collect_blocks_only) { fill_seafile_blocks (sha, o->bl); return clean; } real_path = g_build_path(PATH_SEPERATOR, o->worktree, path, NULL); if (update_wd) { char *new_path; SeafStat st; char *conflict_suffix; /* When creating a conflict directory, we use o->branch2 as conflict * suffix instead of the last changer name of path. * This is because there may be more than one conflicting file * under this directory, each has different changer. */ if (make_room_for_path(o->index, path, real_path, &new_path, o->branch2, &clean) < 0) { g_free (real_path); return clean; } g_free (real_path); /* Checkout an empty dir. */ if (S_ISDIR (mode)) { if (g_mkdir (path, 0777) < 0) g_warning ("Failed to create empty dir %s.\n", path); return 0; } /* We're checking out a clean file in recover merge. * Note that file is clean only when it's added by others. */ if (update_cache && o->recover_merge && seaf_stat(new_path, &st) == 0 && S_ISREG(st.st_mode)) { if (compare_file_content (new_path, &st, sha, o->crypt) == 0) { real_path = new_path; goto update_cache; } /* If the file was checked out and changed by user, we * don't need to check out again, since the user should * know the content of this file. */ g_free (new_path); return clean; } conflict_suffix = get_last_changer_of_file (o->remote_head, path); if (!conflict_suffix) conflict_suffix = g_strdup(o->branch2); rawdata_to_hex(sha, file_id, 20); if (seaf_fs_manager_checkout_file(seaf->fs_mgr, file_id, new_path, mode, o->crypt, conflict_suffix) < 0) { g_warning("Failed to checkout file %s.\n", file_id); g_free(new_path); g_free (conflict_suffix); return clean; } g_free (conflict_suffix); /* replace real_path with new_path. */ real_path = new_path; } update_cache: if (update_cache) add_cacheinfo(o->index, mode, sha, path, real_path, 0, update_wd, ADD_CACHE_OK_TO_ADD); g_free(real_path); return clean; }