r23468: Open registry.tdb with sequence number.
[sfrench/samba-autobuild/.git] / source / registry / reg_db.c
index 31122b858e252629c5670c7ce6d653f5ded93c94..555ae6adda325b10de103a46d128ae8e1d91cc90 100644 (file)
@@ -29,6 +29,9 @@ static TDB_CONTEXT *tdb_reg;
 static int tdb_refcount;
 
 #define VALUE_PREFIX   "SAMBA_REGVAL"
+#define SECDESC_PREFIX  "SAMBA_SECDESC"
+
+#define REG_TDB_FLAGS TDB_SEQNUM
 
 /* List the deepest path into the registry.  All part components will be created.*/
 
@@ -97,6 +100,20 @@ static BOOL init_registry_data( void )
        int i;
        const char *p, *p2;
        UNISTR2 data;
+
+       /*
+        * There are potentially quite a few store operations which are all
+        * indiviually wrapped in tdb transactions. Wrapping them in a single
+        * transaction gives just a single transaction_commit() to actually do
+        * its fsync()s. See tdb/common/transaction.c for info about nested
+        * transaction behaviour.
+        */
+
+       if ( tdb_transaction_start( tdb_reg ) == -1 ) {
+               DEBUG(0, ("init_registry_data: tdb_transaction_start "
+                         "failed\n"));
+               return False;
+       }
        
        /* loop over all of the predefined paths and add each component */
        
@@ -136,14 +153,14 @@ static BOOL init_registry_data( void )
                        
                        if ( !(subkeys = TALLOC_ZERO_P( NULL, REGSUBKEY_CTR )) ) {
                                DEBUG(0,("talloc() failure!\n"));
-                               return False;
+                               goto fail;
                        }
 
                        regdb_fetch_keys( base, subkeys );
                        if ( *subkeyname ) 
                                regsubkey_ctr_addkey( subkeys, subkeyname );
                        if ( !regdb_store_keys( base, subkeys ))
-                               return False;
+                               goto fail;
                        
                        TALLOC_FREE( subkeys );
                }
@@ -154,7 +171,7 @@ static BOOL init_registry_data( void )
        for ( i=0; builtin_registry_values[i].path != NULL; i++ ) {
                if ( !(values = TALLOC_ZERO_P( NULL, REGVAL_CTR )) ) {
                        DEBUG(0,("talloc() failure!\n"));
-                       return False;
+                       goto fail;
                }
 
                regdb_fetch_values( builtin_registry_values[i].path, values );
@@ -191,7 +208,22 @@ static BOOL init_registry_data( void )
                TALLOC_FREE( values );
        }
        
+       if (tdb_transaction_commit( tdb_reg ) == -1) {
+               DEBUG(0, ("init_registry_data: Could not commit "
+                         "transaction\n"));
+               return False;
+       }
+
        return True;
+
+ fail:
+
+       if (tdb_transaction_cancel( tdb_reg ) == -1) {
+               smb_panic("init_registry_data: tdb_transaction_cancel "
+                         "failed\n");
+       }
+
+       return False;
 }
 
 /***********************************************************************
@@ -206,9 +238,9 @@ BOOL regdb_init( void )
        if ( tdb_reg )
                return True;
 
-       if ( !(tdb_reg = tdb_open_log(lock_path("registry.tdb"), 0, TDB_DEFAULT, O_RDWR, 0600)) )
+       if ( !(tdb_reg = tdb_open_log(lock_path("registry.tdb"), 0, REG_TDB_FLAGS, O_RDWR, 0600)) )
        {
-               tdb_reg = tdb_open_log(lock_path("registry.tdb"), 0, TDB_DEFAULT, O_RDWR|O_CREAT, 0600);
+               tdb_reg = tdb_open_log(lock_path("registry.tdb"), 0, REG_TDB_FLAGS, O_RDWR|O_CREAT, 0600);
                if ( !tdb_reg ) {
                        DEBUG(0,("regdb_init: Failed to open registry %s (%s)\n",
                                lock_path("registry.tdb"), strerror(errno) ));
@@ -253,7 +285,7 @@ WERROR regdb_open( void )
        
        become_root();
 
-       tdb_reg = tdb_open_log(lock_path("registry.tdb"), 0, TDB_DEFAULT, O_RDWR, 0600);
+       tdb_reg = tdb_open_log(lock_path("registry.tdb"), 0, REG_TDB_FLAGS, O_RDWR, 0600);
        if ( !tdb_reg ) {
                result = ntstatus_to_werror( map_nt_error_from_unix( errno ) );
                DEBUG(0,("regdb_open: Failed to open %s! (%s)\n", 
@@ -290,6 +322,16 @@ int regdb_close( void )
        return ret;
 }
 
+/***********************************************************************
+ return the tdb sequence number of the registry tdb.
+ this is an indicator for the content of the registry
+ having changed. it will change upon regdb_init, too, though.
+ ***********************************************************************/
+int regdb_get_seqnum(void)
+{
+       return tdb_get_seqnum(tdb_reg);
+}
+
 /***********************************************************************
  Add subkey strings to the registry tdb under a defined key
  fmt is the same format as tdb_pack except this function only supports
@@ -298,8 +340,8 @@ int regdb_close( void )
  
 static BOOL regdb_store_keys_internal( const char *key, REGSUBKEY_CTR *ctr )
 {
-       TDB_DATA kbuf, dbuf;
-       char *buffer;
+       TDB_DATA dbuf;
+       uint8 *buffer;
        int i = 0;
        uint32 len, buflen;
        BOOL ret = True;
@@ -314,7 +356,7 @@ static BOOL regdb_store_keys_internal( const char *key, REGSUBKEY_CTR *ctr )
 
        /* allocate some initial memory */
                
-       if (!(buffer = (char *)SMB_MALLOC(sizeof(pstring)))) {
+       if (!(buffer = (uint8 *)SMB_MALLOC(sizeof(pstring)))) {
                return False;
        }
        buflen = sizeof(pstring);
@@ -330,7 +372,7 @@ static BOOL regdb_store_keys_internal( const char *key, REGSUBKEY_CTR *ctr )
                len += tdb_pack( buffer+len, buflen-len, "f", regsubkey_ctr_specific_key(ctr, i) );
                if ( len > buflen ) {
                        /* allocate some extra space */
-                       if ((buffer = (char *)SMB_REALLOC( buffer, len*2 )) == NULL) {
+                       if ((buffer = (uint8 *)SMB_REALLOC( buffer, len*2 )) == NULL) {
                                DEBUG(0,("regdb_store_keys: Failed to realloc memory of size [%d]\n", len*2));
                                ret = False;
                                goto done;
@@ -343,11 +385,9 @@ static BOOL regdb_store_keys_internal( const char *key, REGSUBKEY_CTR *ctr )
        
        /* finally write out the data */
        
-       kbuf.dptr = keyname;
-       kbuf.dsize = strlen(keyname)+1;
        dbuf.dptr = buffer;
        dbuf.dsize = len;
-       if ( tdb_store( tdb_reg, kbuf, dbuf, TDB_REPLACE ) == -1) {
+       if ( tdb_store_bystring( tdb_reg, keyname, dbuf, TDB_REPLACE ) == -1) {
                ret = False;
                goto done;
        }
@@ -367,15 +407,20 @@ BOOL regdb_store_keys( const char *key, REGSUBKEY_CTR *ctr )
 {
        int num_subkeys, i;
        pstring path;
-       REGSUBKEY_CTR *subkeys, *old_subkeys;
+       REGSUBKEY_CTR *subkeys = NULL, *old_subkeys = NULL;
        char *oldkeyname;
        
+       if ( tdb_transaction_start( tdb_reg ) == -1 ) {
+               DEBUG(0, ("regdb_store_keys: tdb_transaction_start failed\n"));
+               return False;
+       }
+
        /* fetch a list of the old subkeys so we can determine if any were
         * deleted */
        
        if ( !(old_subkeys = TALLOC_ZERO_P( ctr, REGSUBKEY_CTR )) ) {
                DEBUG(0,("regdb_store_keys: talloc() failure!\n"));
-               return False;
+               goto fail;
        }
 
        regdb_fetch_keys( key, old_subkeys );
@@ -384,8 +429,8 @@ BOOL regdb_store_keys( const char *key, REGSUBKEY_CTR *ctr )
        
        if ( !regdb_store_keys_internal( key, ctr ) ) {
                DEBUG(0,("regdb_store_keys: Failed to store new subkey list "
-                        "for parent [%s}\n", key ));
-               return False;
+                        "for parent [%s]\n", key ));
+               goto fail;
        }
        
        /* now delete removed keys */
@@ -393,15 +438,30 @@ BOOL regdb_store_keys( const char *key, REGSUBKEY_CTR *ctr )
        num_subkeys = regsubkey_ctr_numkeys( old_subkeys );
        for ( i=0; i<num_subkeys; i++ ) {
                oldkeyname = regsubkey_ctr_specific_key( old_subkeys, i );
-               if ( !regsubkey_ctr_key_exists( ctr, oldkeyname ) ) {
-                       pstr_sprintf( path, "%s%c%s", key, '/', oldkeyname );
-                       normalize_reg_path( path );
-                       tdb_delete_bystring( tdb_reg, path );
-                       pstr_sprintf( path, "%s/%s/%s", VALUE_PREFIX, key,
-                                     oldkeyname );
-                       normalize_reg_path( path );
-                       tdb_delete_bystring( tdb_reg, path );
+
+               if ( regsubkey_ctr_key_exists( ctr, oldkeyname ) ) {
+                       /*
+                        * It's still around, don't delete
+                        */
+
+                       continue;
+               }
+
+               pstr_sprintf( path, "%s/%s", key, oldkeyname );
+               normalize_reg_path( path );
+               if (tdb_delete_bystring( tdb_reg, path ) == -1) {
+                       DEBUG(1, ("Deleting %s failed\n", path));
+                       goto fail;
                }
+               
+               pstr_sprintf( path, "%s/%s/%s", VALUE_PREFIX, key,
+                             oldkeyname );
+               normalize_reg_path( path );
+
+               /*
+                * Ignore errors here, we might have no values around
+                */
+               tdb_delete_bystring( tdb_reg, path );
        }
 
        TALLOC_FREE( old_subkeys );
@@ -410,28 +470,42 @@ BOOL regdb_store_keys( const char *key, REGSUBKEY_CTR *ctr )
        
        num_subkeys = regsubkey_ctr_numkeys( ctr );
        for ( i=0; i<num_subkeys; i++ ) {
-               pstr_sprintf( path, "%s%c%s", key, '/',
+               pstr_sprintf( path, "%s/%s", key,
                              regsubkey_ctr_specific_key( ctr, i ) );
 
                if ( !(subkeys = TALLOC_ZERO_P( ctr, REGSUBKEY_CTR )) ) {
                        DEBUG(0,("regdb_store_keys: talloc() failure!\n"));
-                       return False;
+                       goto fail;
                }
 
                if ( regdb_fetch_keys( path, subkeys ) == -1 ) {
                        /* create a record with 0 subkeys */
                        if ( !regdb_store_keys_internal( path, subkeys ) ) {
                                DEBUG(0,("regdb_store_keys: Failed to store "
-                                        "new record for key [%s}\n", path ));
-                               TALLOC_FREE( subkeys );
-                               return False;
+                                        "new record for key [%s]\n", path ));
+                               goto fail;
                        }
                }
 
                TALLOC_FREE( subkeys );
        }
-       
+
+       if (tdb_transaction_commit( tdb_reg ) == -1) {
+               DEBUG(0, ("regdb_store_keys: Could not commit transaction\n"));
+               return False;
+       }
+
        return True;
+
+ fail:
+       TALLOC_FREE( old_subkeys );
+       TALLOC_FREE( subkeys );
+
+       if (tdb_transaction_cancel( tdb_reg ) == -1) {
+               smb_panic("regdb_store_keys: tdb_transaction_cancel failed\n");
+       }
+
+       return False;
 }
 
 
@@ -445,7 +519,7 @@ int regdb_fetch_keys( const char* key, REGSUBKEY_CTR *ctr )
        pstring path;
        uint32 num_items;
        TDB_DATA dbuf;
-       char *buf;
+       uint8 *buf;
        uint32 buflen, len;
        int i;
        fstring subkeyname;
@@ -486,7 +560,7 @@ int regdb_fetch_keys( const char* key, REGSUBKEY_CTR *ctr )
  Unpack a list of registry values frem the TDB
  ***************************************************************************/
  
-static int regdb_unpack_values(REGVAL_CTR *values, char *buf, int buflen)
+static int regdb_unpack_values(REGVAL_CTR *values, uint8 *buf, int buflen)
 {
        int             len = 0;
        uint32          type;
@@ -531,7 +605,7 @@ static int regdb_unpack_values(REGVAL_CTR *values, char *buf, int buflen)
  Pack all values in all printer keys
  ***************************************************************************/
  
-static int regdb_pack_values(REGVAL_CTR *values, char *buf, int buflen)
+static int regdb_pack_values(REGVAL_CTR *values, uint8 *buf, int buflen)
 {
        int             len = 0;
        int             i;
@@ -611,7 +685,7 @@ BOOL regdb_store_values( const char *key, REGVAL_CTR *values )
                return False;
        }
        
-       data.dptr = SMB_MALLOC_ARRAY( char, len );
+       data.dptr = SMB_MALLOC_ARRAY( uint8, len );
        data.dsize = len;
        
        len = regdb_pack_values( values, data.dptr, data.dsize );
@@ -621,13 +695,95 @@ BOOL regdb_store_values( const char *key, REGVAL_CTR *values )
        pstr_sprintf( keystr, "%s/%s", VALUE_PREFIX, key );
        normalize_reg_path( keystr );
        
-       ret = tdb_store_bystring(tdb_reg, keystr, data, TDB_REPLACE);
+       ret = tdb_trans_store_bystring(tdb_reg, keystr, data, TDB_REPLACE);
        
        SAFE_FREE( data.dptr );
        
        return ret != -1 ;
 }
 
+void normalize_dbkey(char *key)
+{
+       size_t len = strlen(key);
+       string_sub(key, "\\", "/", len+1);
+       strupper_m(key);
+}
+
+static WERROR regdb_get_secdesc(TALLOC_CTX *mem_ctx, const char *key,
+                               struct security_descriptor **psecdesc)
+{
+       char *tdbkey;
+       TDB_DATA data;
+       NTSTATUS status;
+
+       DEBUG(10, ("regdb_get_secdesc: Getting secdesc of key [%s]\n", key));
+
+       if (asprintf(&tdbkey, "%s/%s", SECDESC_PREFIX, key) == -1) {
+               return WERR_NOMEM;
+       }
+       normalize_dbkey(tdbkey);
+
+        data = tdb_fetch_bystring(tdb_reg, tdbkey);
+       SAFE_FREE(tdbkey);
+
+       if (data.dptr == NULL) {
+               return WERR_BADFILE;
+       }
+
+       status = unmarshall_sec_desc(mem_ctx, (uint8 *)data.dptr, data.dsize,
+                                    psecdesc);
+
+       SAFE_FREE(data.dptr);
+
+       if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MEMORY)) {
+               return WERR_NOMEM;
+       }
+
+       if (!NT_STATUS_IS_OK(status)) {
+               return WERR_REG_CORRUPT;
+       }
+
+       return WERR_OK;
+}
+
+static WERROR regdb_set_secdesc(const char *key,
+                               struct security_descriptor *secdesc)
+{
+       prs_struct ps;
+       TALLOC_CTX *mem_ctx;
+       char *tdbkey;
+       WERROR err = WERR_NOMEM;
+       TDB_DATA tdbdata;
+
+       if (!(mem_ctx = talloc_init("regdb_set_secdesc"))) {
+               return WERR_NOMEM;
+       }
+
+       ZERO_STRUCT(ps);
+
+       if (!(tdbkey = talloc_asprintf(mem_ctx, "%s/%s", SECDESC_PREFIX,
+                                      key))) {
+               goto done;
+       }
+       normalize_dbkey(tdbkey);
+
+       err = ntstatus_to_werror(marshall_sec_desc(mem_ctx, secdesc,
+                                                  &tdbdata.dptr,
+                                                  &tdbdata.dsize));
+       if (!W_ERROR_IS_OK(err)) {
+               goto done;
+       }
+
+       if (tdb_trans_store_bystring(tdb_reg, tdbkey, tdbdata, 0) == -1) {
+               err = ntstatus_to_werror(map_nt_error_from_unix(errno));
+               goto done;
+       }
+
+ done:
+       prs_mem_free(&ps);
+       TALLOC_FREE(mem_ctx);
+       return err;
+}
 
 /* 
  * Table of function pointers for default access
@@ -638,7 +794,7 @@ REGISTRY_OPS regdb_ops = {
        regdb_fetch_values,
        regdb_store_keys,
        regdb_store_values,
-       NULL
+       NULL,
+       regdb_get_secdesc,
+       regdb_set_secdesc
 };
-
-