From da579f5153f7fa1108091fc18b0761e7a0581c33 Mon Sep 17 00:00:00 2001 From: Andreas Baumann Date: Thu, 7 May 2015 15:22:28 +0200 Subject: fixed the rename problem (EEXIST if the destination file exists is wrong, the destination file should get all data and metadata from the source file) --- pgfuse.c | 17 +++++++++++--- pgsql.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ pgsql.h | 2 ++ tests/Makefile | 6 +++++ 4 files changed, 94 insertions(+), 3 deletions(-) diff --git a/pgfuse.c b/pgfuse.c index c77d70f..c4e0ebd 100644 --- a/pgfuse.c +++ b/pgfuse.c @@ -1346,9 +1346,20 @@ static int pgfuse_rename( const char *from, const char *to ) PSQL_ROLLBACK( conn ); RELEASE( conn ); return 0; } else { - /* otherwise bail out */ - PSQL_ROLLBACK( conn ); RELEASE( conn ); - return -EEXIST; + /* otherwise make source file disappear and + * destination file contain the same data + * as the source one (preferably atomic because + * of rename/lockfile tricks) + */ + res = psql_rename_to_existing_file( conn, from_id, to_id, from, to ); + if( res < 0 ) { + PSQL_ROLLBACK( conn ); RELEASE( conn ); + return res; + } + + PSQL_COMMIT( conn ); RELEASE( conn ); + + return res; } } /* TODO: handle all other cases */ diff --git a/pgsql.c b/pgsql.c index 0022603..bfa14d6 100644 --- a/pgsql.c +++ b/pgsql.c @@ -846,6 +846,78 @@ int psql_rollback( PGconn *conn ) return 0; } +int psql_rename_to_existing_file( PGconn *conn, const int64_t from_id, const int64_t to_id, const char *from_path, const char *to_path ) +{ + int64_t param1 = htobe64( to_id ); + int64_t param2 = htobe64( from_id ); + const char *values[2] = { (char *)¶m1, (char *)¶m2 }; + int lengths[2] = { sizeof( param1 ), sizeof( param2 ) }; + int binary[2] = { 1, 1 }; + PGresult *res; + + /* first delete the data of the destination file */ + res = PQexecParams( conn, "DELETE FROM data WHERE dir_id=$1::bigint", + 1, NULL, values, lengths, binary, 1 ); + + if( PQresultStatus( res ) != PGRES_COMMAND_OK ) { + syslog( LOG_ERR, "Error in psql_rename_to_existing_file to remove data of the destination file '%s': %s", + to_path, PQerrorMessage( conn ) ); + PQclear( res ); + return -EIO; + } + + /* the destination file should inherit the data from the source file, + avoid copying stupidly here */ + res = PQexecParams( conn, "UPDATE data SET dir_id=$1::bigint WHERE dir_id=$2::bigint", + 2, NULL, values, lengths, binary, 1 ); + + if( PQresultStatus( res ) != PGRES_COMMAND_OK ) { + syslog( LOG_ERR, "Error in psql_rename_to_existing_file to move data from '%s' to '%s': %s", + from_path, to_path, PQerrorMessage( conn ) ); + PQclear( res ); + return -EIO; + } + + PQclear( res ); + + /* We inherit automatically all metadata from the source + file for the destination file. But we would like to + have the new filename of the destination file. */ + values[0] = (char *)¶m2; + values[1] = to_path; + lengths[1] = strlen( to_path ); + binary[1] = 0; + + res = PQexecParams( conn, "UPDATE dir SET name=$2::varchar WHERE id=$1::bigint", + 2, NULL, values, lengths, binary, 1 ); + + if( PQresultStatus( res ) != PGRES_COMMAND_OK ) { + syslog( LOG_ERR, "Error in psql_rename_to_existing_file to set new name for '%s' to '%s': %s", + from_path, to_path, PQerrorMessage( conn ) ); + PQclear( res ); + return -EIO; + } + + PQclear( res ); + + /* finally we delete the inode entry for the source file */ + + values[0] = (char *)¶m2; + res = PQexecParams( conn, "DELETE FROM dir where id=$1::bigint", + 1, NULL, values, lengths, binary, 1 ); + + if( PQresultStatus( res ) != PGRES_COMMAND_OK ) { + syslog( LOG_ERR, "Error in psql_renamc_existing_file when deleting dir entry for path '%s': %s", + from_path, PQerrorMessage( conn ) ); + PQclear( res ); + return -EIO; + } + + PQclear( res ); + + return 0; +} + int psql_rename( PGconn *conn, const int64_t from_id, const int64_t from_parent_id, const int64_t to_parent_id, const char *rename_to, const char *from, const char *to ) { PgMeta from_parent_meta; diff --git a/pgsql.h b/pgsql.h index abb58ec..918d818 100644 --- a/pgsql.h +++ b/pgsql.h @@ -96,6 +96,8 @@ int psql_truncate( PGconn *conn, const size_t block_size, const int64_t id, cons int psql_rename( PGconn *conn, const int64_t from_id, const int64_t from_parent_id, const int64_t to_parent_id, const char *rename_to, const char *from, const char *to ); +int psql_rename_to_existing_file( PGconn *conn, const int64_t from_id, const int64_t to_id, const char *from_path, const char *to_path ); + size_t psql_get_block_size( PGconn *conn, const size_t block_size ); int64_t psql_get_fs_blocks_used( PGconn *conn ); diff --git a/tests/Makefile b/tests/Makefile index 9170e79..21dedca 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -66,6 +66,12 @@ test: testfsync testpgsql testtypes testbigfile testopen -df -i mnt # test open/creat and flags -./testopen + # move test + -echo "afile" > mnt/afile + -echo "bfile" > mnt/bfile + -mv mnt/afile mnt/bfile + -cat mnt/bfile + -rm mnt/bfile # END: unmount FUSE file system fusermount -u mnt -- cgit v1.2.3-54-g00ecf