summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndreas Baumann <mail@andreasbaumann.cc>2015-05-07 15:22:28 +0200
committerAndreas Baumann <mail@andreasbaumann.cc>2015-05-07 15:22:28 +0200
commitda579f5153f7fa1108091fc18b0761e7a0581c33 (patch)
treeebcfceb3bd98cdd0a93f56eb7d6e38f013c11ad5
parent8ac849f21ed34c447d7744cdaf76c3e10d8045cc (diff)
downloadpgfuse-da579f5153f7fa1108091fc18b0761e7a0581c33.tar.gz
pgfuse-da579f5153f7fa1108091fc18b0761e7a0581c33.tar.bz2
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)
-rw-r--r--pgfuse.c17
-rw-r--r--pgsql.c72
-rw-r--r--pgsql.h2
-rw-r--r--tests/Makefile6
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 *)&param1, (char *)&param2 };
+ 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 *)&param2;
+ 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 *)&param2;
+ 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