r23468: Open registry.tdb with sequence number.
[sfrench/samba-autobuild/.git] / source / registry / reg_db.c
index 64a863ceacd0b09ef44f297790e609a997a4cec2..555ae6adda325b10de103a46d128ae8e1d91cc90 100644 (file)
 #define DBGC_CLASS DBGC_RPC_SRV
 
 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.*/
 
-/* If you want to have a part of the path controlled by the tdb abd part by
+/* If you want to have a part of the path controlled by the tdb and part by
    a virtual registry db (e.g. printing), then you have to list the deepest path.
    For example,"HKLM/SOFTWARE/Microsoft/Windows NT/CurrentVersion/Print" 
    allows the reg_db backend to handle everything up to 
@@ -45,10 +49,12 @@ static const char *builtin_registry_paths[] = {
        KEY_PRINTING,
        KEY_SHARES,
        KEY_EVENTLOG,
+       KEY_SMBCONF,
        "HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Perflib",
        "HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Perflib\\009",
        "HKLM\\SYSTEM\\CurrentControlSet\\Control\\Print\\Monitors",
        "HKLM\\SYSTEM\\CurrentControlSet\\Control\\ProductOptions",
+       "HKLM\\SYSTEM\\CurrentControlSet\\Control\\Terminal Server\\DefaultUserConfiguration",
        "HKLM\\SYSTEM\\CurrentControlSet\\Services\\TcpIp\\Parameters",
        "HKLM\\SYSTEM\\CurrentControlSet\\Services\\Netlogon\\Parameters",
        "HKU",
@@ -89,11 +95,25 @@ static BOOL init_registry_data( void )
 {
        pstring path, base, remaining;
        fstring keyname, subkeyname;
-       REGSUBKEY_CTR   subkeys;
-       REGVAL_CTR values;
+       REGSUBKEY_CTR *subkeys;
+       REGVAL_CTR *values;
        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 */
        
@@ -131,27 +151,38 @@ static BOOL init_registry_data( void )
                           we are about to update the record.  We just want any 
                           subkeys already present */
                        
-                       regsubkey_ctr_init( &subkeys );
-                                                  
-                       regdb_fetch_keys( base, &subkeys );
+                       if ( !(subkeys = TALLOC_ZERO_P( NULL, REGSUBKEY_CTR )) ) {
+                               DEBUG(0,("talloc() failure!\n"));
+                               goto fail;
+                       }
+
+                       regdb_fetch_keys( base, subkeys );
                        if ( *subkeyname ) 
-                               regsubkey_ctr_addkey( &subkeys, subkeyname );
-                       if ( !regdb_store_keys( base, &subkeys ))
-                               return False;
+                               regsubkey_ctr_addkey( subkeys, subkeyname );
+                       if ( !regdb_store_keys( base, subkeys ))
+                               goto fail;
                        
-                       regsubkey_ctr_destroy( &subkeys );
+                       TALLOC_FREE( subkeys );
                }
        }
 
        /* loop over all of the predefined values and add each component */
        
        for ( i=0; builtin_registry_values[i].path != NULL; i++ ) {
-               regval_ctr_init( &values );
-               
-               regdb_fetch_values( builtin_registry_values[i].path, &values );
-               switch( builtin_registry_values[i].type ) {
+               if ( !(values = TALLOC_ZERO_P( NULL, REGVAL_CTR )) ) {
+                       DEBUG(0,("talloc() failure!\n"));
+                       goto fail;
+               }
+
+               regdb_fetch_values( builtin_registry_values[i].path, values );
+
+               /* preserve existing values across restarts.  Only add new ones */
+
+               if ( !regval_ctr_key_exists( values, builtin_registry_values[i].valuename ) ) 
+               {
+                       switch( builtin_registry_values[i].type ) {
                        case REG_DWORD:
-                               regval_ctr_addvalue( &values, 
+                               regval_ctr_addvalue( values, 
                                                     builtin_registry_values[i].valuename,
                                                     REG_DWORD,
                                                     (char*)&builtin_registry_values[i].data.dw_value,
@@ -160,7 +191,7 @@ static BOOL init_registry_data( void )
                                
                        case REG_SZ:
                                init_unistr2( &data, builtin_registry_values[i].data.string, UNI_STR_TERMINATE);
-                               regval_ctr_addvalue( &values, 
+                               regval_ctr_addvalue( values, 
                                                     builtin_registry_values[i].valuename,
                                                     REG_SZ,
                                                     (char*)data.buffer,
@@ -170,20 +201,36 @@ static BOOL init_registry_data( void )
                        default:
                                DEBUG(0,("init_registry_data: invalid value type in builtin_registry_values [%d]\n",
                                        builtin_registry_values[i].type));
+                       }
+                       regdb_store_values( builtin_registry_values[i].path, values );
                }
-               regdb_store_values( builtin_registry_values[i].path, &values );
                
-               regval_ctr_destroy( &values );
+               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;
 }
 
 /***********************************************************************
  Open the registry database
  ***********************************************************************/
  
-BOOL init_registry_db( void )
+BOOL regdb_init( void )
 {
        const char *vstring = "INFO/version";
        uint32 vers_id;
@@ -191,36 +238,100 @@ BOOL init_registry_db( void )
        if ( tdb_reg )
                return True;
 
-       /* placeholder tdb; reinit upon startup */
-       
-       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,("init_registry: Failed to open registry %s (%s)\n",
+                       DEBUG(0,("regdb_init: Failed to open registry %s (%s)\n",
                                lock_path("registry.tdb"), strerror(errno) ));
                        return False;
                }
                
-               DEBUG(10,("init_registry: Successfully created registry tdb\n"));
+               DEBUG(10,("regdb_init: Successfully created registry tdb\n"));
        }
+
+       tdb_refcount = 1;
                
 
        vers_id = tdb_fetch_int32(tdb_reg, vstring);
 
        if ( vers_id != REGVER_V1 ) {
+               /* any upgrade code here if needed */
+       }
 
-               /* create the registry here */
+       /* always setup the necessary keys and values */
 
-               if ( !init_registry_data() ) {
-                       DEBUG(0,("init_registry: Failed to initiailize data in registry!\n"));
-                       return False;
-               }
+       if ( !init_registry_data() ) {
+               DEBUG(0,("init_registry: Failed to initialize data in registry!\n"));
+               return False;
        }
 
        return True;
 }
 
+/***********************************************************************
+ Open the registry.  Must already have been initialized by regdb_init()
+ ***********************************************************************/
+
+WERROR regdb_open( void )
+{
+       WERROR result = WERR_OK;
+
+       if ( tdb_reg ) {
+               DEBUG(10,("regdb_open: incrementing refcount (%d)\n", tdb_refcount));
+               tdb_refcount++;
+               return WERR_OK;
+       }
+       
+       become_root();
+
+       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", 
+                       lock_path("registry.tdb"), strerror(errno) ));
+       }
+
+       unbecome_root();
+
+       tdb_refcount = 1;
+       DEBUG(10,("regdb_open: refcount reset (%d)\n", tdb_refcount));
+
+       return result;
+}
+
+/***********************************************************************
+ ***********************************************************************/
+
+int regdb_close( void )
+{
+       int ret;
+
+       tdb_refcount--;
+
+       DEBUG(10,("regdb_close: decrementing refcount (%d)\n", tdb_refcount));
+
+       if ( tdb_refcount > 0 )
+               return 0;
+
+       SMB_ASSERT( tdb_refcount >= 0 );
+
+       ret = tdb_close( tdb_reg );
+       tdb_reg = NULL;
+
+       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
@@ -229,8 +340,8 @@ BOOL init_registry_db( void )
  
 static BOOL regdb_store_keys_internal( const char *key, REGSUBKEY_CTR *ctr )
 {
-       TDB_DATA kbuf, dbuf;
-       char *buffer, *tmpbuf;
+       TDB_DATA dbuf;
+       uint8 *buffer;
        int i = 0;
        uint32 len, buflen;
        BOOL ret = True;
@@ -245,7 +356,9 @@ static BOOL regdb_store_keys_internal( const char *key, REGSUBKEY_CTR *ctr )
 
        /* allocate some initial memory */
                
-       buffer = SMB_MALLOC(sizeof(pstring));
+       if (!(buffer = (uint8 *)SMB_MALLOC(sizeof(pstring)))) {
+               return False;
+       }
        buflen = sizeof(pstring);
        len = 0;
        
@@ -259,12 +372,11 @@ 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 ((tmpbuf = 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;
                        }
-                       buffer = tmpbuf;
                        buflen = len*2;
                                        
                        len = tdb_pack( buffer+len, buflen-len, "f", regsubkey_ctr_specific_key(ctr, i) );
@@ -273,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;
        }
@@ -297,53 +407,105 @@ 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;
        
-       /* fetch a list of the old subkeys so we can determine if any were deleted */
+       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 */
        
-       regsubkey_ctr_init( &old_subkeys );
-       regdb_fetch_keys( key, &old_subkeys );
+       if ( !(old_subkeys = TALLOC_ZERO_P( ctr, REGSUBKEY_CTR )) ) {
+               DEBUG(0,("regdb_store_keys: talloc() failure!\n"));
+               goto fail;
+       }
+
+       regdb_fetch_keys( key, old_subkeys );
        
        /* store the subkey list for the parent */
        
        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;
+               DEBUG(0,("regdb_store_keys: Failed to store new subkey list "
+                        "for parent [%s]\n", key ));
+               goto fail;
        }
        
        /* now delete removed keys */
        
-       num_subkeys = regsubkey_ctr_numkeys( &old_subkeys );
+       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 );
+               oldkeyname = regsubkey_ctr_specific_key( old_subkeys, i );
+
+               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 );
        }
 
-       regsubkey_ctr_destroy( &old_subkeys );
+       TALLOC_FREE( old_subkeys );
        
        /* now create records for any subkeys that don't already exist */
        
        num_subkeys = regsubkey_ctr_numkeys( ctr );
        for ( i=0; i<num_subkeys; i++ ) {
-               pstr_sprintf( path, "%s%c%s", key, '/', regsubkey_ctr_specific_key( ctr, i ) );
-               regsubkey_ctr_init( &subkeys );
-               if ( regdb_fetch_keys( path, &subkeys ) == -1 ) {
+               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"));
+                       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 ));
-                               regsubkey_ctr_destroy( &subkeys );
-                               return False;
+                       if ( !regdb_store_keys_internal( path, subkeys ) ) {
+                               DEBUG(0,("regdb_store_keys: Failed to store "
+                                        "new record for key [%s]\n", path ));
+                               goto fail;
                        }
                }
-               regsubkey_ctr_destroy( &subkeys );
+
+               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;
 }
 
 
@@ -357,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;
@@ -398,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;
@@ -443,16 +605,18 @@ 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;
        REGISTRY_VALUE  *val;
-       int             num_values = regval_ctr_numvals( values );
+       int             num_values;
 
        if ( !values )
                return 0;
 
+       num_values = regval_ctr_numvals( values );
+
        /* pack the number of values first */
        
        len += tdb_pack( buf+len, buflen-len, "d", num_values );
@@ -521,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 );
@@ -531,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
@@ -548,7 +794,7 @@ REGISTRY_OPS regdb_ops = {
        regdb_fetch_values,
        regdb_store_keys,
        regdb_store_values,
-       NULL
+       NULL,
+       regdb_get_secdesc,
+       regdb_set_secdesc
 };
-
-