summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile4
-rw-r--r--pgfuse.c39
-rw-r--r--pgsql.c102
-rw-r--r--schema.sql4
-rw-r--r--testpgsql.c4
5 files changed, 119 insertions, 34 deletions
diff --git a/Makefile b/Makefile
index 4a98878..1fec2c1 100644
--- a/Makefile
+++ b/Makefile
@@ -64,6 +64,10 @@ test: pgfuse testfsync testpgsql
-rmdir mnt/dir/dir2/bfile
# test fdatasync and fsync
./testfsync
+ # show times of dirs, files and symlinks
+ -stat mnt/dir/dir2/afile
+ -stat mnt/dir/dir3
+ -stat mnt/dir/dir2/clink
# show filesystem stats (statvfs)
df -k mnt
df -i mnt
diff --git a/pgfuse.c b/pgfuse.c
index 4406ecc..4bb443e 100644
--- a/pgfuse.c
+++ b/pgfuse.c
@@ -59,6 +59,8 @@ typedef struct PgFuseData {
int read_only; /* whether the mount point is read-only */
} PgFuseData;
+/* --- helper functions --- */
+
static struct timespec now( void )
{
int res;
@@ -79,6 +81,8 @@ static struct timespec now( void )
return s;
}
+/* --- implementation of FUSE hooks --- */
+
static void *pgfuse_init( struct fuse_conn_info *conn )
{
PgFuseData *data = (PgFuseData *)fuse_get_context( )->private_data;
@@ -179,6 +183,9 @@ static int pgfuse_getattr( const char *path, struct stat *stbuf )
/* set rights to the user running 'pgfuse' */
stbuf->st_uid = meta.uid;
stbuf->st_gid = meta.gid;
+ stbuf->st_atime = meta.atime.tv_sec;
+ stbuf->st_mtime = meta.mtime.tv_sec;
+ stbuf->st_ctime = meta.ctime.tv_sec;
return 0;
}
@@ -292,8 +299,8 @@ static int pgfuse_create( const char *path, mode_t mode, struct fuse_file_info *
meta.uid = geteuid( );
meta.gid = getegid( );
meta.ctime = now( );
- meta.atime = now( );
- meta.mtime = now( );
+ meta.mtime = meta.ctime;
+ meta.atime = meta.ctime;
res = psql_create_file( data->conn, parent_id, path, new_file, meta );
if( res < 0 ) {
@@ -506,8 +513,8 @@ static int pgfuse_mkdir( const char *path, mode_t mode )
meta.uid = geteuid( );
meta.gid = getegid( );
meta.ctime = now( );
- meta.atime = now( );
- meta.mtime = now( );
+ meta.mtime = meta.ctime;
+ meta.atime = meta.ctime;
res = psql_create_dir( data->conn, parent_id, path, new_dir, meta );
@@ -955,6 +962,9 @@ static int pgfuse_symlink( const char *from, const char *to )
/* TODO: use FUSE context */
meta.uid = geteuid( );
meta.gid = getegid( );
+ meta.ctime = now( );
+ meta.mtime = meta.ctime;
+ meta.atime = meta.ctime;
res = psql_create_file( data->conn, parent_id, to, symlink, meta );
if( res < 0 ) {
@@ -1185,6 +1195,7 @@ int main( int argc, char *argv[] )
struct fuse_args args = FUSE_ARGS_INIT( argc, argv );
PgFuse pgfuse;
PgFuseData userdata;
+ const char *value;
memset( &pgfuse, 0, sizeof( pgfuse ) );
@@ -1221,6 +1232,26 @@ int main( int argc, char *argv[] )
PQfinish( conn );
exit( EXIT_FAILURE );
}
+
+ /* test storage of timestamps (expecting uint64 as it is the
+ * standard for PostgreSQL 8.4 or newer). Otherwise bail out
+ * currently..
+ */
+ value = PQparameterStatus( conn, "integer_datetimes" );
+ if( value == NULL ) {
+ fprintf( stderr, "PQ param integer_datetimes not available?\n"
+ "You use a too old version of PostgreSQL..can't continue.\n" );
+ PQfinish( conn );
+ return 1;
+ }
+
+ if( strcmp( value, "on" ) != 0 ) {
+ fprintf( stderr, "Expecting UINT64 for timestamps, not doubles. You may use an old version of PostgreSQL (<8.4)\n"
+ "or PostgreSQL has been compiled with the deprecated compile option '--disable-integer-datetimes'\n" );
+ PQfinish( conn );
+ return 1;
+ }
+
PQfinish( conn );
openlog( basename( argv[0] ), LOG_PID, LOG_USER );
diff --git a/pgsql.c b/pgsql.c
index f588319..44a09cd 100644
--- a/pgsql.c
+++ b/pgsql.c
@@ -23,19 +23,46 @@
#include <syslog.h> /* for ERR_XXX */
#include <errno.h> /* for ENOENT and friends */
#include <arpa/inet.h> /* for htonl, ntohl */
+#include <stdint.h> /* for uint64_t */
+#include <endian.h> /* for be64toh (GNU/BSD-ism, but easy to port if necessary) */
+
+/* --- helper functions --- */
+
+/* January 1, 2000, 00:00:00 UTC (in Unix epoch seconds) */
+#define POSTGRES_EPOCH_DATE 946684800
+
+static uint64_t convert_to_timestamp( struct timespec t )
+{
+ return htobe64( ( (uint64_t)t.tv_sec - POSTGRES_EPOCH_DATE ) * 1000000 + t.tv_nsec / 1000 );
+}
+
+static struct timespec convert_from_timestamp( uint64_t raw )
+{
+ uint64_t t;
+ struct timespec ts;
+
+ t = be64toh( raw );
+
+ ts.tv_sec = POSTGRES_EPOCH_DATE + t / 1000000;
+ ts.tv_nsec = ( t % 1000000 ) * 1000;
+
+ return ts;
+}
+
+/* --- postgresql implementation --- */
int psql_get_meta( PGconn *conn, const char *path, PgMeta *meta )
{
PGresult *res;
int idx;
- char *iptr;
+ char *data;
int id;
const char *values[1] = { path };
int lengths[1] = { strlen( path ) };
int binary[1] = { 1 };
- res = PQexecParams( conn, "SELECT id, size, mode, uid, gid FROM dir WHERE path = $1::varchar",
+ res = PQexecParams( conn, "SELECT id, size, mode, uid, gid, ctime, mtime, atime FROM dir WHERE path = $1::varchar",
1, NULL, values, lengths, binary, 1 );
if( PQresultStatus( res ) != PGRES_TUPLES_OK ) {
@@ -56,24 +83,36 @@ int psql_get_meta( PGconn *conn, const char *path, PgMeta *meta )
}
idx = PQfnumber( res, "id" );
- iptr = PQgetvalue( res, 0, idx );
- id = ntohl( *( (uint32_t *)iptr ) );
+ data = PQgetvalue( res, 0, idx );
+ id = ntohl( *( (uint32_t *)data ) );
idx = PQfnumber( res, "size" );
- iptr = PQgetvalue( res, 0, idx );
- meta->size = ntohl( *( (uint32_t *)iptr ) );
+ data = PQgetvalue( res, 0, idx );
+ meta->size = ntohl( *( (uint32_t *)data ) );
idx = PQfnumber( res, "mode" );
- iptr = PQgetvalue( res, 0, idx );
- meta->mode = ntohl( *( (uint32_t *)iptr ) );
+ data = PQgetvalue( res, 0, idx );
+ meta->mode = ntohl( *( (uint32_t *)data ) );
idx = PQfnumber( res, "uid" );
- iptr = PQgetvalue( res, 0, idx );
- meta->uid = ntohl( *( (uint32_t *)iptr ) );
+ data = PQgetvalue( res, 0, idx );
+ meta->uid = ntohl( *( (uint32_t *)data ) );
idx = PQfnumber( res, "gid" );
- iptr = PQgetvalue( res, 0, idx );
- meta->gid = ntohl( *( (uint32_t *)iptr ) );
+ data = PQgetvalue( res, 0, idx );
+ meta->gid = ntohl( *( (uint32_t *)data ) );
+
+ idx = PQfnumber( res, "ctime" );
+ data = PQgetvalue( res, 0, idx );
+ meta->ctime = convert_from_timestamp( *( (uint64_t *)data ) );
+
+ idx = PQfnumber( res, "mtime" );
+ data = PQgetvalue( res, 0, idx );
+ meta->mtime = convert_from_timestamp( *( (uint64_t *)data ) );
+
+ idx = PQfnumber( res, "atime" );
+ data = PQgetvalue( res, 0, idx );
+ meta->atime = convert_from_timestamp( *( (uint64_t *)data ) );
PQclear( res );
@@ -87,13 +126,16 @@ int psql_write_meta( PGconn *conn, const int id, const char *path, PgMeta meta )
int param3 = htonl( meta.mode );
int param4 = htonl( meta.uid );
int param5 = htonl( meta.gid );
- const char *values[5] = { (char *)&param1, (char *)&param2, (char *)&param3, (char *)&param4, (char *)&param5 };
- int lengths[5] = { sizeof( param1 ), sizeof( param2 ), sizeof( param3 ), sizeof( param4 ), sizeof( param5 ) };
- int binary[5] = { 1, 1, 1, 1, 1 };
+ uint64_t param6 = convert_to_timestamp( meta.ctime );
+ uint64_t param7 = convert_to_timestamp( meta.mtime );
+ uint64_t param8 = convert_to_timestamp( meta.atime );
+ const char *values[8] = { (const char *)&param1, (const char *)&param2, (const char *)&param3, (const char *)&param4, (const char *)&param5, (const char *)&param6, (const char *)&param7, (const char *)&param8 };
+ int lengths[8] = { sizeof( param1 ), sizeof( param2 ), sizeof( param3 ), sizeof( param4 ), sizeof( param5 ), sizeof( param6 ), sizeof( param7 ), sizeof( param8 ) };
+ int binary[8] = { 1, 1, 1, 1, 1, 1, 1, 1 };
PGresult *res;
- res = PQexecParams( conn, "UPDATE dir SET size=$2::int4, mode=$3::int4, uid=$4::int4, gid=$5::int4 WHERE id=$1::int4",
- 5, NULL, values, lengths, binary, 1 );
+ res = PQexecParams( conn, "UPDATE dir SET size=$2::int4, mode=$3::int4, uid=$4::int4, gid=$5::int4, ctime=$6::timestamp, mtime=$7::timestamp, atime=$8::timestamp WHERE id=$1::int4",
+ 8, NULL, values, lengths, binary, 1 );
if( PQresultStatus( res ) != PGRES_COMMAND_OK ) {
syslog( LOG_ERR, "Error in psql_write_meta for file '%s': %s", path, PQerrorMessage( conn ) );
@@ -113,13 +155,16 @@ int psql_create_file( PGconn *conn, const int parent_id, const char *path, const
int param3 = htonl( meta.mode );
int param4 = htonl( meta.uid );
int param5 = htonl( meta.gid );
- const char *values[7] = { (const char *)&param1, new_file, path, (const char *)&param2, (const char *)&param3, (const char *)&param4, (const char *)&param5 };
- int lengths[7] = { sizeof( param1 ), strlen( new_file ), strlen( path ), sizeof( param2 ), sizeof( param3 ), sizeof( param4 ), sizeof( param5 ) };
- int binary[7] = { 1, 0, 0, 1, 1, 1, 1 };
+ uint64_t param6 = convert_to_timestamp( meta.ctime );
+ uint64_t param7 = convert_to_timestamp( meta.mtime );
+ uint64_t param8 = convert_to_timestamp( meta.atime );
+ const char *values[10] = { (const char *)&param1, new_file, path, (const char *)&param2, (const char *)&param3, (const char *)&param4, (const char *)&param5, (const char *)&param6, (const char *)&param7, (const char *)&param8 };
+ int lengths[10] = { sizeof( param1 ), strlen( new_file ), strlen( path ), sizeof( param2 ), sizeof( param3 ), sizeof( param4 ), sizeof( param5 ), sizeof( param6 ), sizeof( param7 ), sizeof( param8 ) };
+ int binary[10] = { 1, 0, 0, 1, 1, 1, 1, 1, 1, 1 };
PGresult *res;
- res = PQexecParams( conn, "INSERT INTO dir( parent_id, name, path, size, mode, uid, gid ) VALUES ($1::int4, $2::varchar, $3::varchar, $4::int4, $5::int4, $6::int4, $7::int4 )",
- 7, NULL, values, lengths, binary, 1 );
+ res = PQexecParams( conn, "INSERT INTO dir( parent_id, name, path, size, mode, uid, gid, ctime, mtime, atime ) VALUES ($1::int4, $2::varchar, $3::varchar, $4::int4, $5::int4, $6::int4, $7::int4, $8::timestamp, $9::timestamp, $10::timestamp )",
+ 10, NULL, values, lengths, binary, 1 );
if( PQresultStatus( res ) != PGRES_COMMAND_OK ) {
syslog( LOG_ERR, "Error in psql_create_file for path '%s': %s",
@@ -206,13 +251,16 @@ int psql_create_dir( PGconn *conn, const int parent_id, const char *path, const
int param2 = htonl( meta.mode );
int param3 = htonl( meta.uid );
int param4 = htonl( meta.gid );
- const char *values[6] = { (char *)&param1, new_dir, path, (char *)&param2, (char *)&param3, (char *)&param4 };
- int lengths[6] = { sizeof( param1 ), strlen( new_dir ), strlen( path ), sizeof( param2 ), sizeof( param3 ), sizeof( param4 ) };
- int binary[6] = { 1, 0, 0, 1, 1, 1 };
+ uint64_t param5 = convert_to_timestamp( meta.ctime );
+ uint64_t param6 = convert_to_timestamp( meta.mtime );
+ uint64_t param7 = convert_to_timestamp( meta.atime );
+ const char *values[9] = { (const char *)&param1, new_dir, path, (const char *)&param2, (const char *)&param3, (const char *)&param4, (const char *)&param5, (const char *)&param6, (const char *)&param7 };
+ int lengths[9] = { sizeof( param1 ), strlen( new_dir ), strlen( path ), sizeof( param2 ), sizeof( param3 ), sizeof( param4 ), sizeof( param5 ), sizeof( param6 ), sizeof( param7 ) };
+ int binary[9] = { 1, 0, 0, 1, 1, 1, 1, 1, 1 };
PGresult *res;
- res = PQexecParams( conn, "INSERT INTO dir( parent_id, name, path, mode, uid, gid ) VALUES ($1::int4, $2::varchar, $3::varchar, $4::int4, $5::int4, $6::int4 )",
- 6, NULL, values, lengths, binary, 1 );
+ res = PQexecParams( conn, "INSERT INTO dir( parent_id, name, path, mode, uid, gid, ctime, mtime, atime ) VALUES ($1::int4, $2::varchar, $3::varchar, $4::int4, $5::int4, $6::int4, $7::timestamp, $8::timestamp, $9::timestamp )",
+ 9, NULL, values, lengths, binary, 1 );
if( PQresultStatus( res ) != PGRES_COMMAND_OK ) {
syslog( LOG_ERR, "Error in psql_create_dir for path '%s': %s", path, PQerrorMessage( conn ) );
diff --git a/schema.sql b/schema.sql
index cefc4ea..0b54009 100644
--- a/schema.sql
+++ b/schema.sql
@@ -47,5 +47,5 @@ CREATE OR REPLACE RULE "dir_remove" AS ON
-- self-referencing anchor for root directory
-- 16895 = S_IFDIR and 0777 permissions
-- TODO: should be done from outside, see note above
-INSERT INTO dir( id, parent_id, name, path, size, mode, uid, gid )
- VALUES( 0, 0, '/', '/', 0, 16895, 0, 0 );
+INSERT INTO dir( id, parent_id, name, path, size, mode, uid, gid, ctime, mtime, atime )
+ VALUES( 0, 0, '/', '/', 0, 16895, 0, 0, NOW( ), NOW( ), NOW( ) );
diff --git a/testpgsql.c b/testpgsql.c
index 3efa383..b4e943f 100644
--- a/testpgsql.c
+++ b/testpgsql.c
@@ -22,7 +22,7 @@
#include <stdbool.h> /* for bool */
#include <stdint.h> /* for uint64_t */
#include <endian.h> /* for be64toh (GNU/BSD-ism, but easy to port if necessary) */
-#include <sys/time.h> /* for struct timespec */
+#include <sys/time.h> /* for struct timespec, gettimeofday */
/* January 1, 2000, 00:00:00 UTC (in Unix epoch seconds) */
#define POSTGRES_EPOCH_DATE 946684800
@@ -135,6 +135,8 @@ int main( int argc, char *argv[] )
time_select.tv_sec = POSTGRES_EPOCH_DATE + t_select / 1000000;
time_select.tv_nsec = ( t_select % 1000000 ) * 1000;
+
+ now = get_now( );
printf( "now passed as param: %lu.%lu, now from database: %lu.%lu, now computed: %lu.%lu\n",
time_select.tv_sec, time_select.tv_nsec, time_db.tv_sec, time_db.tv_nsec, now.tv_sec, now.tv_nsec );