Exemple #1
0
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);
}
Exemple #2
0
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;
}
Exemple #3
0
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);
}
Exemple #4
0
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;
}
Exemple #5
0
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;
}
Exemple #6
0
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);
}
Exemple #7
0
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;
}
Exemple #8
0
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);
}
Exemple #9
0
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;
}
Exemple #10
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);
}
Exemple #11
0
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);
}
Exemple #12
0
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);
}
Exemple #13
0
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);
    }

}
Exemple #14
0
/* 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;
}
Exemple #15
0
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;
}
Exemple #16
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;
}
Exemple #17
0
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;
}
Exemple #18
0
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);
}
Exemple #19
0
/* 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;
}
Exemple #20
0
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);
}
Exemple #21
0
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;
}
Exemple #22
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;
}
Exemple #23
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;
}
Exemple #24
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;
}
Exemple #25
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;
}
Exemple #26
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);
}
Exemple #27
0
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;
}