s4 upgradeprovision: Add a function for schema reloading
[mat/samba.git] / source4 / scripting / bin / upgradeprovision
index ae84c9586b08b3922c66927224fe7c36e6faa4fe..11efae02d65cee7cc3c9c98b3e26ee6a16d5edfb 100755 (executable)
@@ -1,6 +1,7 @@
-#!/usr/bin/python
+#!/usr/bin/env python
+# vim: expandtab
 #
-# Copyright (C) Matthieu Patou <mat@matws.net> 2009
+# Copyright (C) Matthieu Patou <mat@matws.net> 2009 - 2010
 #
 # Based on provision a Samba4 server by
 # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2008
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 
-import getopt
-import shutil
+import logging
 import optparse
 import os
+import shutil
 import sys
-import random
-import string
-import re
-import base64
 import tempfile
-# Find right directory when running from source tree
+import re
+import traceback
+# Allow to run from s4 source directory (without installing samba)
 sys.path.insert(0, "bin/python")
 
-from base64 import b64encode
-
+import ldb
 import samba
+import samba.getopt as options
+
+from base64 import b64encode
 from samba.credentials import DONT_USE_KERBEROS
 from samba.auth import system_session, admin_session
-from samba import Ldb, DS_DOMAIN_FUNCTION_2000, DS_DOMAIN_FUNCTION_2003, DS_DOMAIN_FUNCTION_2008, DS_DC_FUNCTION_2008_R2
-from ldb import SCOPE_SUBTREE, SCOPE_ONELEVEL, SCOPE_BASE, LdbError
-import ldb
-import samba.getopt as options
-from samba.samdb import SamDB
-from samba import param
-from samba import glue
-from samba.provision import  ProvisionNames,provision_paths_from_lp,find_setup_dir,FILL_FULL,provision, get_domain_descriptor, get_config_descriptor, secretsdb_self_join
-from samba.provisionexceptions import ProvisioningError
-from samba.schema import get_dnsyntax_attributes, get_linked_attributes, Schema, get_schema_descriptor
-from samba.dcerpc import misc, security
-from samba.ndr import ndr_pack, ndr_unpack
-from samba.dcerpc.misc import SEC_CHAN_BDC
-
-replace=2^ldb.FLAG_MOD_REPLACE
-add=2^ldb.FLAG_MOD_ADD
-delete=2^ldb.FLAG_MOD_DELETE
+from ldb import (SCOPE_SUBTREE, SCOPE_BASE,
+                FLAG_MOD_REPLACE, FLAG_MOD_ADD, FLAG_MOD_DELETE,
+                MessageElement, Message, Dn)
+from samba import param, dsdb
+from samba.provision import (find_setup_dir, get_domain_descriptor,
+                            get_config_descriptor,
+                            ProvisioningError, get_last_provision_usn,
+                            get_max_usn, update_provision_usn)
+from samba.schema import get_linked_attributes, Schema, get_schema_descriptor
+from samba.dcerpc import security, drsblobs, xattr
+from samba.ndr import ndr_unpack
+from samba.upgradehelpers import (dn_sort, get_paths, newprovision,
+                                 find_provision_key_parameters, get_ldbs,
+                                 usn_in_range, identic_rename, get_diff_sddls,
+                                 update_secrets, CHANGE, ERROR, SIMPLE,
+                                 CHANGEALL, GUESS, CHANGESD, PROVISION,
+                                 updateOEMInfo, getOEMInfo, update_gpo,
+                                 delta_update_basesamdb, update_policyids,
+                                 update_machine_account_password,
+                                 search_constructed_attrs_stored,
+                                 increment_calculated_keyversion_number)
+
+replace=2**FLAG_MOD_REPLACE
+add=2**FLAG_MOD_ADD
+delete=2**FLAG_MOD_DELETE
+never=0
+
+
+# Will be modified during provision to tell if default sd has been modified
+# somehow ...
 
 #Errors are always logged
-ERROR =        -1
-SIMPLE =       0x00
-CHANGE =       0x01
-CHANGESD =     0x02
-GUESS =        0x04
-PROVISION =    0x08
-CHANGEALL =    0xff
-
-# Attributes that not copied from the reference provision even if they do not exists in the destination object
-# This is most probably because they are populated automatcally when object is created
-hashAttrNotCopied = {  "dn": 1,"whenCreated": 1,"whenChanged": 1,"objectGUID": 1,"replPropertyMetaData": 1,"uSNChanged": 1,\
-                                               "uSNCreated": 1,"parentGUID": 1,"objectCategory": 1,"distinguishedName": 1,\
-                                               "showInAdvancedViewOnly": 1,"instanceType": 1, "cn": 1, "msDS-Behavior-Version":1, "nextRid":1,\
-                                               "nTMixedDomain": 1,"versionNumber":1, "lmPwdHistory":1, "pwdLastSet": 1, "ntPwdHistory":1, "unicodePwd":1,\
-                                               "dBCSPwd":1,"supplementalCredentials":1,"gPCUserExtensionNames":1, "gPCMachineExtensionNames":1,\
-                                               "maxPwdAge":1, "mail":1, "secret":1,"possibleInferiors":1, "sAMAccountType":1}
-
-# Usually for an object that already exists we do not overwrite attributes as they might have been changed for good
-# reasons. Anyway for a few of thems it's mandatory to replace them otherwise the provision will be broken somehow.
-hashOverwrittenAtt = {  "prefixMap": replace, "systemMayContain": replace,"systemOnly":replace, "searchFlags":replace,\
-                                                "mayContain":replace,  "systemFlags":replace,
-                                                "oEMInformation":replace, "operatingSystemVersion":replace, "adminPropertyPages":replace,
-                                                "defaultSecurityDescriptor": replace}
-backlinked = []
 
+__docformat__ = "restructuredText"
+
+# Attributes that are never copied from the reference provision (even if they
+# do not exist in the destination object).
+# This is most probably because they are populated automatcally when object is
+# created
+# This also apply to imported object from reference provision
+hashAttrNotCopied = {   "dn": 1, "whenCreated": 1, "whenChanged": 1,
+                        "objectGUID": 1, "uSNCreated": 1,
+                        "replPropertyMetaData": 1, "uSNChanged": 1,
+                        "parentGUID": 1, "objectCategory": 1,
+                        "distinguishedName": 1, "nTMixedDomain": 1,
+                        "showInAdvancedViewOnly": 1, "instanceType": 1,
+                        "msDS-Behavior-Version":1, "nextRid":1, "cn": 1,
+                        "versionNumber":1, "lmPwdHistory":1, "pwdLastSet": 1,
+                        "ntPwdHistory":1, "unicodePwd":1,"dBCSPwd":1,
+                        "supplementalCredentials":1, "gPCUserExtensionNames":1,
+                        "gPCMachineExtensionNames":1,"maxPwdAge":1, "secret":1,
+                        "possibleInferiors":1, "privilege":1,
+                        "sAMAccountType":1 }
+
+# Usually for an object that already exists we do not overwrite attributes as
+# they might have been changed for good reasons. Anyway for a few of them it's
+# mandatory to replace them otherwise the provision will be broken somehow.
+# But for attribute that are just missing we do not have to specify them as the default
+# behavior is to add missing attribute
+hashOverwrittenAtt = {  "prefixMap": replace, "systemMayContain": replace,
+                        "systemOnly":replace, "searchFlags":replace,
+                        "mayContain":replace, "systemFlags":replace+add,
+                        "description":replace, "operatingSystemVersion":replace,
+                        "adminPropertyPages":replace, "groupType":replace,
+                        "wellKnownObjects":replace, "privilege":never,
+                        "defaultSecurityDescriptor": replace,
+                        "rIDAvailablePool": never,
+                        "defaultSecurityDescriptor": replace + add,
+                        "isMemberOfPartialAttributeSet": delete,
+                        "attributeDisplayNames": replace + add}
+
+
+backlinked = []
+forwardlinked = set()
+dn_syntax_att = []
 def define_what_to_log(opts):
-       what = 0
-       if opts.debugchange:
-               what = what | CHANGE
-       if opts.debugchangesd:
-               what = what | CHANGESD
-       if opts.debugguess:
-               what = what | GUESS
-       if opts.debugprovision:
-               what = what | PROVISION
-       if opts.debugall:
-               what = what | CHANGEALL
-       return what
+    what = 0
+    if opts.debugchange:
+        what = what | CHANGE
+    if opts.debugchangesd:
+        what = what | CHANGESD
+    if opts.debugguess:
+        what = what | GUESS
+    if opts.debugprovision:
+        what = what | PROVISION
+    if opts.debugall:
+        what = what | CHANGEALL
+    return what
 
 
 parser = optparse.OptionParser("provision [options]")
@@ -105,30 +137,44 @@ parser.add_option_group(options.VersionOptions(parser))
 credopts = options.CredentialsOptions(parser)
 parser.add_option_group(credopts)
 parser.add_option("--setupdir", type="string", metavar="DIR",
-                                       help="directory with setup files")
+                  help="directory with setup files")
 parser.add_option("--debugprovision", help="Debug provision", action="store_true")
-parser.add_option("--debugguess", help="Print information on what is different but won't be changed", action="store_true")
-parser.add_option("--debugchange", help="Print information on what is different but won't be changed", action="store_true")
-parser.add_option("--debugchangesd", help="Print information security descriptors differences", action="store_true")
-parser.add_option("--debugall", help="Print all available information (very verbose)", action="store_true")
-parser.add_option("--full", help="Perform full upgrade of the samdb (schema, configuration, new objects, ...", action="store_true")
+parser.add_option("--debugguess", action="store_true",
+                  help="Print information on what is different but won't be changed")
+parser.add_option("--debugchange", action="store_true",
+                  help="Print information on what is different but won't be changed")
+parser.add_option("--debugchangesd", action="store_true",
+                  help="Print information security descriptors differences")
+parser.add_option("--debugall", action="store_true",
+                  help="Print all available information (very verbose)")
+parser.add_option("--resetfileacl", action="store_true",
+                  help="Force a reset on filesystem acls in sysvol / netlogon share")
+parser.add_option("--full", action="store_true",
+                  help="Perform full upgrade of the samdb (schema, configuration, new objects, ...")
 
 opts = parser.parse_args()[0]
 
+handler = logging.StreamHandler(sys.stdout)
+upgrade_logger = logging.getLogger("upgradeprovision")
+upgrade_logger.setLevel(logging.INFO)
+
+upgrade_logger.addHandler(handler)
+
+provision_logger = logging.getLogger("provision")
+provision_logger.addHandler(handler)
+
 whatToLog = define_what_to_log(opts)
 
-def messageprovision(text):
-       """print a message if quiet is not set."""
-       if opts.debugprovision or opts.debugall:
-               print text
+def message(what, text):
+    """Print a message if this message type has been selected to be printed
 
-def message(what,text):
-       """print a message if quiet is not set."""
-       if (whatToLog & what) or (what <= 0 ):
-               print text
+    :param what: Category of the message
+    :param text: Message to print """
+    if (whatToLog & what) or what <= 0:
+        upgrade_logger.info("%s", text)
 
 if len(sys.argv) == 1:
-       opts.interactive = True
+    opts.interactive = True
 lp = sambaopts.get_loadparm()
 smbconf = lp.configfile
 
@@ -138,646 +184,1511 @@ setup_dir = opts.setupdir
 if setup_dir is None:
     setup_dir = find_setup_dir()
 
-session = system_session()
-
-# Create an array of backlinked attributes
-def populate_backlink(newpaths,creds,session,schemadn):
-       newsam_ldb = Ldb(newpaths.samdb, session_info=session, credentials=creds,lp=lp)
-       backlinked.extend(get_linked_attributes(ldb.Dn(newsam_ldb,str(schemadn)),newsam_ldb).values())
-
-# Get Paths for important objects (ldb, keytabs ...)
-def get_paths(targetdir=None,smbconf=None):
-       if targetdir is not None:
-               if (not os.path.exists(os.path.join(targetdir, "etc"))):
-                       os.makedirs(os.path.join(targetdir, "etc"))
-               smbconf = os.path.join(targetdir, "etc", "smb.conf")
-       if smbconf is None:
-                       smbconf = param.default_path()
-
-       if not os.path.exists(smbconf):
-               message(ERROR,"Unable to find smb.conf ..")
-               parser.print_usage()
-               sys.exit(1)
-
-       lp = param.LoadParm()
-       lp.load(smbconf)
-# Normaly we need the domain name for this function but for our needs it's pointless
-       paths = provision_paths_from_lp(lp,"foo")
-       return paths
-
-# This function guess(fetch) informations needed to make a fresh provision from the current provision
-# It includes: realm, workgroup, partitions, netbiosname, domain guid, ...
-def guess_names_from_current_provision(credentials,session_info,paths):
-       lp = param.LoadParm()
-       lp.load(paths.smbconf)
-       names = ProvisionNames()
-       # NT domain, kerberos realm, root dn, domain dn, domain dns name
-       names.domain = string.upper(lp.get("workgroup"))
-       names.realm = lp.get("realm")
-       basedn = "DC=" + names.realm.replace(".",",DC=")
-       names.dnsdomain = names.realm
-       names.realm = string.upper(names.realm)
-       # netbiosname
-       secrets_ldb = Ldb(paths.secrets, session_info=session_info, credentials=credentials,lp=lp, options=["modules:samba_secrets"])
-       # Get the netbiosname first (could be obtained from smb.conf in theory)
-       attrs = ["sAMAccountName"]
-       res = secrets_ldb.search(expression="(flatname=%s)"%names.domain,base="CN=Primary Domains", scope=SCOPE_SUBTREE, attrs=attrs)
-       names.netbiosname = str(res[0]["sAMAccountName"]).replace("$","")
-
-       names.smbconf = smbconf
-       #It's important here to let ldb load with the old module or it's quite certain that the LDB won't load ...
-       samdb = Ldb(paths.samdb, session_info=session_info,
-                   credentials=credentials, lp=lp, options=["modules:samba_dsdb"])
-
-       # That's a bit simplistic but it's ok as long as we have only 3 partitions
-       attrs2 = ["defaultNamingContext", "schemaNamingContext","configurationNamingContext","rootDomainNamingContext"]
-       res2 = samdb.search(expression="(objectClass=*)",base="", scope=SCOPE_BASE, attrs=attrs2)
-
-       names.configdn = res2[0]["configurationNamingContext"]
-       configdn = str(names.configdn)
-       names.schemadn = res2[0]["schemaNamingContext"]
-       if not (ldb.Dn(samdb, basedn) == (ldb.Dn(samdb, res2[0]["defaultNamingContext"][0]))):
-               raise ProvisioningError(("basedn in %s (%s) and from %s (%s) is not the same ..." % (paths.samdb, str(res2[0]["defaultNamingContext"][0]), paths.smbconf, basedn)))
-
-       names.domaindn=res2[0]["defaultNamingContext"]
-       names.rootdn=res2[0]["rootDomainNamingContext"]
-       # default site name
-       attrs3 = ["cn"]
-       res3= samdb.search(expression="(objectClass=*)",base="CN=Sites,"+configdn, scope=SCOPE_ONELEVEL, attrs=attrs3)
-       names.sitename = str(res3[0]["cn"])
-
-       # dns hostname and server dn
-       attrs4 = ["dNSHostName"]
-       res4= samdb.search(expression="(CN=%s)"%names.netbiosname,base="OU=Domain Controllers,"+basedn, \
-                                               scope=SCOPE_ONELEVEL, attrs=attrs4)
-       names.hostname = str(res4[0]["dNSHostName"]).replace("."+names.dnsdomain,"")
-
-       server_res = samdb.search(expression="serverReference=%s"%res4[0].dn, attrs=[], base=configdn)
-       names.serverdn = server_res[0].dn
-
-       # invocation id/objectguid
-       res5 = samdb.search(expression="(objectClass=*)",base="CN=NTDS Settings,%s" % str(names.serverdn), scope=SCOPE_BASE, attrs=["invocationID","objectGUID"])
-       names.invocation = str(ndr_unpack( misc.GUID,res5[0]["invocationId"][0]))
-       names.ntdsguid = str(ndr_unpack( misc.GUID,res5[0]["objectGUID"][0]))
-
-       # domain guid/sid
-       attrs6 = ["objectGUID", "objectSid","msDS-Behavior-Version" ]
-       res6 = samdb.search(expression="(objectClass=*)",base=basedn, scope=SCOPE_BASE, attrs=attrs6)
-       names.domainguid = str(ndr_unpack( misc.GUID,res6[0]["objectGUID"][0]))
-       names.domainsid = ndr_unpack( security.dom_sid,res6[0]["objectSid"][0])
-       if res6[0].get("msDS-Behavior-Version") == None or int(res6[0]["msDS-Behavior-Version"][0]) < DS_DOMAIN_FUNCTION_2000:
-               names.domainlevel = DS_DOMAIN_FUNCTION_2000
-       else:
-               names.domainlevel = int(res6[0]["msDS-Behavior-Version"][0])
-
-       # policy guid
-       attrs7 = ["cn","displayName"]
-       res7 = samdb.search(expression="(displayName=Default Domain Policy)",base="CN=Policies,CN=System,"+basedn, \
-                                                       scope=SCOPE_ONELEVEL, attrs=attrs7)
-       names.policyid = str(res7[0]["cn"]).replace("{","").replace("}","")
-       # dc policy guid
-       attrs8 = ["cn","displayName"]
-       res8 = samdb.search(expression="(displayName=Default Domain Controllers Policy)",base="CN=Policies,CN=System,"+basedn, \
-                                                       scope=SCOPE_ONELEVEL, attrs=attrs7)
-       if len(res8) == 1:
-               names.policyid_dc = str(res8[0]["cn"]).replace("{","").replace("}","")
-       else:
-               names.policyid_dc = None
-
-
-       return names
-
-# Debug a little bit
-def print_names(names):
-       message(GUESS, "rootdn      :"+str(names.rootdn))
-       message(GUESS, "configdn    :"+str(names.configdn))
-       message(GUESS, "schemadn    :"+str(names.schemadn))
-       message(GUESS, "serverdn    :"+str(names.serverdn))
-       message(GUESS, "netbiosname :"+names.netbiosname)
-       message(GUESS, "defaultsite :"+names.sitename)
-       message(GUESS, "dnsdomain   :"+names.dnsdomain)
-       message(GUESS, "hostname    :"+names.hostname)
-       message(GUESS, "domain      :"+names.domain)
-       message(GUESS, "realm       :"+names.realm)
-       message(GUESS, "invocationid:"+names.invocation)
-       message(GUESS, "policyguid  :"+names.policyid)
-       message(GUESS, "policyguiddc:"+str(names.policyid_dc))
-       message(GUESS, "domainsid   :"+str(names.domainsid))
-       message(GUESS, "domainguid  :"+names.domainguid)
-       message(GUESS, "ntdsguid    :"+names.ntdsguid)
-       message(GUESS, "domainlevel :"+str(names.domainlevel))
-
-# Create a fresh new reference provision
-# This provision will be the reference for knowing what has changed in the
-# since the latest upgrade in the current provision
-def newprovision(names,setup_dir,creds,session,smbconf):
-       message(SIMPLE, "Creating a reference provision")
-       provdir=tempfile.mkdtemp(dir=paths.private_dir, prefix="referenceprovision")
-       if os.path.isdir(provdir):
-               rmall(provdir)
-       logstd=os.path.join(provdir,"log.std")
-       os.chdir(os.path.join(setup_dir,".."))
-       os.mkdir(provdir)
-       os.close(2)
-       sys.stderr = open("%s/provision.log"%provdir, "w")
-       message(PROVISION, "Reference provision stored in %s"%provdir)
-       message(PROVISION, "STDERR message of provision will be logged in %s/provision.log"%provdir)
-       sys.stderr = open("/dev/stdout", "w")
-       provision(setup_dir, messageprovision,
-               session, creds, smbconf=smbconf, targetdir=provdir,
-               samdb_fill=FILL_FULL, realm=names.realm, domain=names.domain,
-               domainguid=names.domainguid, domainsid=str(names.domainsid),ntdsguid=names.ntdsguid,
-               policyguid=names.policyid,policyguid_dc=names.policyid_dc,hostname=names.netbiosname,
-               hostip=None, hostip6=None,
-               invocationid=names.invocation, adminpass=None,
-               krbtgtpass=None, machinepass=None,
-               dnspass=None, root=None, nobody=None,
-               wheel=None, users=None,
-               serverrole="domain controller",
-               ldap_backend_extra_port=None,
-               backend_type=None,
-               ldapadminpass=None,
-               ol_mmr_urls=None,
-               slapd_path=None,
-               setup_ds_path=None,
-               nosync=None,
-               dom_for_fun_level=names.domainlevel,
-               ldap_dryrun_mode=None)
-       return provdir
-
-# This function sorts two dn in the lexicographical order and put higher level DN before
-# So given the dns cn=bar,cn=foo and cn=foo the later will be return as smaller (-1) as it has less
-# level
-def dn_sort(x,y):
-       p = re.compile(r'(?<!\\),')
-       tab1 = p.split(str(x))
-       tab2 = p.split(str(y))
-       min = 0
-       if (len(tab1) > len(tab2)):
-               min = len(tab2)
-       elif (len(tab1) < len(tab2)):
-               min = len(tab1)
-       else:
-               min = len(tab1)
-       len1=len(tab1)-1
-       len2=len(tab2)-1
-       space = " "
-       # Note: python range go up to upper limit but do not include it
-       for i in range(0,min):
-               ret=cmp(tab1[len1-i],tab2[len2-i])
-               if(ret != 0):
-                       return ret
-               else:
-                       if(i==min-1):
-                               if(len1==len2):
-                                       message(ERROR,"PB PB PB"+space.join(tab1)+" / "+space.join(tab2))
-                               if(len1>len2):
-                                       return 1
-                               else:
-                                       return -1
-       return ret
-
-# check from security descriptors modifications return 1 if it is 0 otherwise
-# it also populate hash structure for later use in the upgrade process
-def handle_security_desc(ischema,att,msgElt,hashallSD,old,new):
-       if ischema == 1 and att == "defaultSecurityDescriptor"  and msgElt.flags() == ldb.FLAG_MOD_REPLACE:
-               hashSD = {}
-               hashSD["oldSD"] = old[0][att]
-               hashSD["newSD"] = new[0][att]
-               hashallSD[str(old[0].dn)] = hashSD
-               return 0
-       if att == "nTSecurityDescriptor"  and msgElt.flags() == ldb.FLAG_MOD_REPLACE:
-               if ischema == 0:
-                       hashSD = {}
-                       hashSD["oldSD"] =  ndr_unpack(security.descriptor,str(old[0][att]))
-                       hashSD["newSD"] =  ndr_unpack(security.descriptor,str(new[0][att]))
-                       hashallSD[str(old[0].dn)] = hashSD
-               return 1
-       return 0
-
-# Hangle special cases ... That's when we want to update an attribute only
-# if it has a certain value or if it's for a certain object or
-# a class of object.
-# It can be also if we want to do a merge of value instead of a simple replace
-def handle_special_case(att,delta,new,old,ischema):
-       flag = delta.get(att).flags()
-       if (att == "gPLink" or att == "gPCFileSysPath") and flag ==  ldb.FLAG_MOD_REPLACE and str(new[0].dn).lower() == str(old[0].dn).lower():
-               delta.remove(att)
-               return 1
-       if att == "forceLogoff":
-               ref=0x8000000000000000
-               oldval=int(old[0][att][0])
-               newval=int(new[0][att][0])
-               ref == old and ref == abs(new)
-               return 1
-       if (att == "adminDisplayName" or att == "adminDescription") and ischema:
-               return 1
-       if (str(old[0].dn) == "CN=Samba4-Local-Domain,%s"%(str(names.schemadn)) and att == "defaultObjectCategory" and flag  == ldb.FLAG_MOD_REPLACE):
-               return 1
-       if (str(old[0].dn) == "CN=S-1-5-11,CN=ForeignSecurityPrincipals,%s"%(str(names.rootdn)) and att == "description" and flag  == ldb.FLAG_MOD_DELETE):
-               return 1
-       if (str(old[0].dn) == "CN=Title,%s"%(str(names.schemadn)) and att == "rangeUpper" and flag  == ldb.FLAG_MOD_REPLACE):
-               return 1
-       if ( (att == "member" or att == "servicePrincipalName") and flag  == ldb.FLAG_MOD_REPLACE):
-
-               hash = {}
-               newval = []
-               changeDelta=0
-               for elem in old[0][att]:
-                       hash[str(elem)]=1
-                       newval.append(str(elem))
-
-               for elem in new[0][att]:
-                       if not hash.has_key(str(elem)):
-                               changeDelta=1
-                               newval.append(str(elem))
-               if changeDelta == 1:
-                       delta[att] = ldb.MessageElement(newval, ldb.FLAG_MOD_REPLACE, att)
-               else:
-                       delta.remove(att)
-               return 1
-       if (str(old[0].dn) == "%s"%(str(names.rootdn)) and att == "subRefs" and flag  == ldb.FLAG_MOD_REPLACE):
-               return 1
-       if str(delta.dn).endswith("CN=DisplaySpecifiers,%s"%names.configdn):
-               return 1
-       return 0
-
-def update_secrets(newpaths,paths,creds,session):
-       message(SIMPLE,"update secrets.ldb")
-       newsecrets_ldb = Ldb(newpaths.secrets, session_info=session, credentials=creds,lp=lp)
-       secrets_ldb = Ldb(paths.secrets, session_info=session, credentials=creds,lp=lp, options=["modules:samba_secrets"])
-       res = newsecrets_ldb.search(expression="dn=@MODULES",base="", scope=SCOPE_SUBTREE)
-       res2 = secrets_ldb.search(expression="dn=@MODULES",base="", scope=SCOPE_SUBTREE)
-       delta = secrets_ldb.msg_diff(res2[0],res[0])
-       delta.dn = res2[0].dn
-       secrets_ldb.modify(delta)
-
-       newsecrets_ldb = Ldb(newpaths.secrets, session_info=session, credentials=creds,lp=lp)
-       secrets_ldb = Ldb(paths.secrets, session_info=session, credentials=creds,lp=lp)
-       res = newsecrets_ldb.search(expression="objectClass=top",base="", scope=SCOPE_SUBTREE,attrs=["dn"])
-       res2 = secrets_ldb.search(expression="objectClass=top",base="", scope=SCOPE_SUBTREE,attrs=["dn"])
-       hash_new = {}
-       hash = {}
-       listMissing = []
-       listPresent = []
-
-       empty = ldb.Message()
-       for i in range(0,len(res)):
-               hash_new[str(res[i]["dn"]).lower()] = res[i]["dn"]
-
-       # Create a hash for speeding the search of existing object in the current provision
-       for i in range(0,len(res2)):
-               hash[str(res2[i]["dn"]).lower()] = res2[i]["dn"]
-
-       for k in hash_new.keys():
-               if not hash.has_key(k):
-                       listMissing.append(hash_new[k])
-               else:
-                       listPresent.append(hash_new[k])
-       for entry in listMissing:
-               res = newsecrets_ldb.search(expression="dn=%s"%entry,base="", scope=SCOPE_SUBTREE)
-               res2 = secrets_ldb.search(expression="dn=%s"%entry,base="", scope=SCOPE_SUBTREE)
-               delta = secrets_ldb.msg_diff(empty,res[0])
-               for att in hashAttrNotCopied.keys():
-                       delta.remove(att)
-               message(CHANGE,"Entry %s is missing from secrets.ldb"%res[0].dn)
-               for att in delta:
-                       message(CHANGE," Adding attribute %s"%att)
-               delta.dn = res[0].dn
-               secrets_ldb.add(delta)
-
-       for entry in listPresent:
-               res = newsecrets_ldb.search(expression="dn=%s"%entry,base="", scope=SCOPE_SUBTREE)
-               res2 = secrets_ldb.search(expression="dn=%s"%entry,base="", scope=SCOPE_SUBTREE)
-               delta = secrets_ldb.msg_diff(res2[0],res[0])
-               i=0
-               for att in hashAttrNotCopied.keys():
-                       delta.remove(att)
-               for att in delta:
-                       i = i + 1
-
-                       if att == "name":
-                               message(CHANGE,"Found attribute name on  %s, must rename the DN "%(res2[0].dn))
-                               secrets_ldb.rename(res2[0].dn,ldb.Dn(secrets_ldb,"%sfoo"%str(res2[0].dn)))
-                               secrets_ldb.rename(ldb.Dn(secrets_ldb,"%sfoo"%str(res2[0].dn)),res2[0].dn)
-                       else:
-                               delta.remove(att)
-
-
-       for entry in listPresent:
-               res = newsecrets_ldb.search(expression="dn=%s"%entry,base="", scope=SCOPE_SUBTREE)
-               res2 = secrets_ldb.search(expression="dn=%s"%entry,base="", scope=SCOPE_SUBTREE)
-               delta = secrets_ldb.msg_diff(res2[0],res[0])
-               i=0
-               for att in hashAttrNotCopied.keys():
-                       delta.remove(att)
-               for att in delta:
-                       i = i + 1
-                       if att != "dn":
-                               message(CHANGE," Adding/Changing attribute %s to %s"%(att,res2[0].dn))
-
-               delta.dn = res2[0].dn
-               secrets_ldb.modify(delta)
-
-
-# Check difference between the current provision and the reference provision.
-# It looks for all object which base DN is name if ischema is false then scan is done in
-# cross partition mode.
-# If ischema is true, then special handling is done for dealing with schema
-def check_diff_name(newpaths,paths,creds,session,basedn,names,ischema):
-       hash_new = {}
-       hash = {}
-       hashallSD = {}
-       listMissing = []
-       listPresent = []
-       res = []
-       res2 = []
-       # Connect to the reference provision and get all the attribute in the partition referred by name
-       newsam_ldb = Ldb(newpaths.samdb, session_info=session, credentials=creds,lp=lp)
-       sam_ldb = Ldb(paths.samdb, session_info=session, credentials=creds,lp=lp, options=["modules:samba_dsdb"])
-       sam_ldb.transaction_start()
-       if ischema:
-               res = newsam_ldb.search(expression="objectClass=*",base=basedn, scope=SCOPE_SUBTREE,attrs=["dn"])
-               res2 = sam_ldb.search(expression="objectClass=*",base=basedn, scope=SCOPE_SUBTREE,attrs=["dn"])
-       else:
-               res = newsam_ldb.search(expression="objectClass=*",base=basedn, scope=SCOPE_SUBTREE,attrs=["dn"],controls=["search_options:1:2"])
-               res2 = sam_ldb.search(expression="objectClass=*",base=basedn, scope=SCOPE_SUBTREE,attrs=["dn"],controls=["search_options:1:2"])
-
-       sam_ldb.transaction_commit()
-       # Create a hash for speeding the search of new object
-       for i in range(0,len(res)):
-               hash_new[str(res[i]["dn"]).lower()] = res[i]["dn"]
-
-       # Create a hash for speeding the search of existing object in the current provision
-       for i in range(0,len(res2)):
-               hash[str(res2[i]["dn"]).lower()] = res2[i]["dn"]
-
-       for k in hash_new.keys():
-               if not hash.has_key(k):
-                       listMissing.append(hash_new[k])
-               else:
-                       listPresent.append(hash_new[k])
-
-       # Sort the missing object in order to have object of the lowest level first (which can be
-       # containers for higher level objects)
-       listMissing.sort(dn_sort)
-       listPresent.sort(dn_sort)
-
-       if ischema:
-               # The following lines (up to the for loop) is to load the up to date schema into our current LDB
-               # a complete schema is needed as the insertion of attributes and class is done against it
-               # and the schema is self validated
-               # The double ldb open and schema validation is taken from the initial provision script
-               # it's not certain that it is really needed ....
-               sam_ldb = Ldb(session_info=session, credentials=creds, lp=lp)
-               schema = Schema(setup_path, names.domainsid, schemadn=basedn, serverdn=str(names.serverdn))
-               # Load the schema from the one we computed earlier
-               sam_ldb.set_schema_from_ldb(schema.ldb)
-               # And now we can connect to the DB - the schema won't be loaded from the DB
-               sam_ldb.connect(paths.samdb)
-       else:
-               sam_ldb = Ldb(paths.samdb, session_info=session, credentials=creds,lp=lp, options=["modules:samba_dsdb"])
-
-       sam_ldb.transaction_start()
-
-       empty = ldb.Message()
-       message(SIMPLE,"There are %d missing objects"%(len(listMissing)))
-       for dn in listMissing:
-               res = newsam_ldb.search(expression="dn=%s"%(str(dn)),base=basedn, scope=SCOPE_SUBTREE,controls=["search_options:1:2"])
-               delta = sam_ldb.msg_diff(empty,res[0])
-               for att in hashAttrNotCopied.keys():
-                       delta.remove(att)
-               for att in backlinked:
-                       delta.remove(att)
-               delta.dn = dn
-
-               sam_ldb.add(delta,["relax:0"])
-
-       changed = 0
-       for dn in listPresent:
-               res = newsam_ldb.search(expression="dn=%s"%(str(dn)),base=basedn, scope=SCOPE_SUBTREE,controls=["search_options:1:2"])
-               res2 = sam_ldb.search(expression="dn=%s"%(str(dn)),base=basedn, scope=SCOPE_SUBTREE,controls=["search_options:1:2"])
-               delta = sam_ldb.msg_diff(res2[0],res[0])
-               for att in hashAttrNotCopied.keys():
-                       delta.remove(att)
-               for att in backlinked:
-                       delta.remove(att)
-               delta.remove("parentGUID")
-               nb = 0
-               for att in delta:
-                       msgElt = delta.get(att)
-                       if att == "dn":
-                               continue
-                       if handle_security_desc(ischema,att,msgElt,hashallSD,res2,res):
-                               delta.remove(att)
-                               continue
-                       if (not hashOverwrittenAtt.has_key(att) or not (hashOverwrittenAtt.get(att)&2^msgElt.flags())):
-                               if  handle_special_case(att,delta,res,res2,ischema)==0 and msgElt.flags()!=ldb.FLAG_MOD_ADD:
-                                       i = 0
-                                       if opts.debugchange:
-                                               message(CHANGE, "dn= "+str(dn)+ " "+att + " with flag "+str(msgElt.flags())+ " is not allowed to be changed/removed, I discard this change ...")
-                                               for e in range(0,len(res2[0][att])):
-                                                       message(CHANGE,"old %d : %s"%(i,str(res2[0][att][e])))
-                                               if msgElt.flags() == 2:
-                                                       i = 0
-                                                       for e in range(0,len(res[0][att])):
-                                                               message(CHANGE,"new %d : %s"%(i,str(res[0][att][e])))
-                                       delta.remove(att)
-               delta.dn = dn
-               if len(delta.items()) >1:
-                       attributes=",".join(delta.keys())
-                       message(CHANGE,"%s is different from the reference one, changed attributes: %s"%(dn,attributes))
-                       changed = changed + 1
-                       sam_ldb.modify(delta)
-
-       sam_ldb.transaction_commit()
-       message(SIMPLE,"There are %d changed objects"%(changed))
-       return hashallSD
-
-# Check that SD are correct
-def check_updated_sd(newpaths,paths,creds,session,names):
-       newsam_ldb = Ldb(newpaths.samdb, session_info=session, credentials=creds,lp=lp)
-       sam_ldb = Ldb(paths.samdb, session_info=session, credentials=creds,lp=lp)
-       res = newsam_ldb.search(expression="objectClass=*",base=str(names.rootdn), scope=SCOPE_SUBTREE,attrs=["dn","nTSecurityDescriptor"],controls=["search_options:1:2"])
-       res2 = sam_ldb.search(expression="objectClass=*",base=str(names.rootdn), scope=SCOPE_SUBTREE,attrs=["dn","nTSecurityDescriptor"],controls=["search_options:1:2"])
-       hash_new = {}
-       for i in range(0,len(res)):
-               hash_new[str(res[i]["dn"]).lower()] = ndr_unpack(security.descriptor,str(res[i]["nTSecurityDescriptor"])).as_sddl(names.domainsid)
-
-       for i in range(0,len(res2)):
-               key = str(res2[i]["dn"]).lower()
-               if hash_new.has_key(key):
-                       sddl = ndr_unpack(security.descriptor,str(res2[i]["nTSecurityDescriptor"])).as_sddl(names.domainsid)
-                       if sddl != hash_new[key]:
-                               print "%s new sddl/sddl in ref"%key
-                               print "%s\n%s"%(sddl,hash_new[key])
-
-# Simple update method for updating the SD that rely on the fact that nobody should have modified the SD
-# This assumption is safe right now (alpha9) but should be removed asap
-def update_sd(paths,creds,session,names):
-       sam_ldb = Ldb(paths.samdb, session_info=session, credentials=creds,lp=lp,options=["modules:samba_dsdb"])
-       sam_ldb.transaction_start()
-       # First update the SD for the rootdn
-       sam_ldb.set_session_info(session)
-       res = sam_ldb.search(expression="objectClass=*",base=str(names.rootdn), scope=SCOPE_BASE,attrs=["dn","whenCreated"],controls=["search_options:1:2"])
-       delta = ldb.Message()
-       delta.dn = ldb.Dn(sam_ldb,str(res[0]["dn"]))
-       descr = get_domain_descriptor(names.domainsid)
-       delta["nTSecurityDescriptor"] = ldb.MessageElement( descr,ldb.FLAG_MOD_REPLACE,"nTSecurityDescriptor" )
-       sam_ldb.modify(delta,["recalculate_sd:0"])
-       # Then the config dn
-       res = sam_ldb.search(expression="objectClass=*",base=str(names.configdn), scope=SCOPE_BASE,attrs=["dn","whenCreated"],controls=["search_options:1:2"])
-       delta = ldb.Message()
-       delta.dn = ldb.Dn(sam_ldb,str(res[0]["dn"]))
-       descr = get_config_descriptor(names.domainsid)
-       delta["nTSecurityDescriptor"] = ldb.MessageElement( descr,ldb.FLAG_MOD_REPLACE,"nTSecurityDescriptor" )
-       sam_ldb.modify(delta,["recalculate_sd:0"])
-       # Then the schema dn
-       res = sam_ldb.search(expression="objectClass=*",base=str(names.schemadn), scope=SCOPE_BASE,attrs=["dn","whenCreated"],controls=["search_options:1:2"])
-       delta = ldb.Message()
-       delta.dn = ldb.Dn(sam_ldb,str(res[0]["dn"]))
-       descr = get_schema_descriptor(names.domainsid)
-       delta["nTSecurityDescriptor"] = ldb.MessageElement( descr,ldb.FLAG_MOD_REPLACE,"nTSecurityDescriptor" )
-       sam_ldb.modify(delta,["recalculate_sd:0"])
-
-       # Then the rest
-       hash = {}
-       res = sam_ldb.search(expression="objectClass=*",base=str(names.rootdn), scope=SCOPE_SUBTREE,attrs=["dn","whenCreated"],controls=["search_options:1:2"])
-       for obj in res:
-               if not (str(obj["dn"]) == str(names.rootdn) or
-                       str(obj["dn"]) == str(names.configdn) or \
-                       str(obj["dn"]) == str(names.schemadn)):
-                       hash[str(obj["dn"])] = obj["whenCreated"]
-
-       listkeys = hash.keys()
-       listkeys.sort(dn_sort)
-
-       for key in listkeys:
-               try:
-                       delta = ldb.Message()
-                       delta.dn = ldb.Dn(sam_ldb,key)
-                       delta["whenCreated"] = ldb.MessageElement( hash[key],ldb.FLAG_MOD_REPLACE,"whenCreated" )
-                       sam_ldb.modify(delta,["recalculate_sd:0"])
-               except:
-                       sam_ldb.transaction_cancel()
-                       res = sam_ldb.search(expression="objectClass=*",base=str(names.rootdn), scope=SCOPE_SUBTREE,attrs=["dn","nTSecurityDescriptor"],controls=["search_options:1:2"])
-                       print "bad stuff" +ndr_unpack(security.descriptor,str(res[0]["nTSecurityDescriptor"])).as_sddl(names.domainsid)
-                       return
-       sam_ldb.transaction_commit()
-
-def rmall(topdir):
-       for root, dirs, files in os.walk(topdir, topdown=False):
-               for name in files:
-                       os.remove(os.path.join(root, name))
-               for name in dirs:
-                       os.rmdir(os.path.join(root, name))
-       os.rmdir(topdir)
-
-
-def update_basesamdb(newpaths,paths,names):
-       message(SIMPLE,"Copy samdb")
-       shutil.copy(newpaths.samdb,paths.samdb)
-
-       message(SIMPLE,"Update partitions filename if needed")
-       schemaldb=os.path.join(paths.private_dir,"schema.ldb")
-       configldb=os.path.join(paths.private_dir,"configuration.ldb")
-       usersldb=os.path.join(paths.private_dir,"users.ldb")
-       samldbdir=os.path.join(paths.private_dir,"sam.ldb.d")
-
-       if not os.path.isdir(samldbdir):
-               os.mkdir(samldbdir)
-               os.chmod(samldbdir,0700)
-       if os.path.isfile(schemaldb):
-               shutil.copy(schemaldb,os.path.join(samldbdir,"%s.ldb"%str(names.schemadn).upper()))
-               os.remove(schemaldb)
-       if os.path.isfile(usersldb):
-               shutil.copy(usersldb,os.path.join(samldbdir,"%s.ldb"%str(names.rootdn).upper()))
-               os.remove(usersldb)
-       if os.path.isfile(configldb):
-               shutil.copy(configldb,os.path.join(samldbdir,"%s.ldb"%str(names.configdn).upper()))
-               os.remove(configldb)
-
-def update_privilege(newpaths,paths):
-       message(SIMPLE,"Copy privilege")
-       shutil.copy(os.path.join(newpaths.private_dir,"privilege.ldb"),os.path.join(paths.private_dir,"privilege.ldb"))
-
-# For each partition check the differences
-def update_samdb(newpaths,paths,creds,session,names):
-
-       message(SIMPLE, "Doing schema update")
-       hashdef = check_diff_name(newpaths,paths,creds,session,str(names.schemadn),names,1)
-       message(SIMPLE,"Done with schema update")
-       message(SIMPLE,"Scanning whole provision for updates and additions")
-       hashSD = check_diff_name(newpaths,paths,creds,session,str(names.rootdn),names,0)
-       message(SIMPLE,"Done with scanning")
-
-def update_machine_account_password(paths,creds,session,names):
-
-       secrets_ldb = Ldb(paths.secrets, session_info=session, credentials=creds,lp=lp)
-       secrets_ldb.transaction_start()
-       secrets_msg = secrets_ldb.search(expression=("samAccountName=%s$" % names.netbiosname), attrs=["secureChannelType"])
-       sam_ldb = Ldb(paths.samdb, session_info=session, credentials=creds,lp=lp)
-       sam_ldb.transaction_start()
-       if int(secrets_msg[0]["secureChannelType"][0]) == SEC_CHAN_BDC:
-               res = sam_ldb.search(expression=("samAccountName=%s$" % names.netbiosname), attrs=[])
-               assert(len(res) == 1)
-
-               msg = ldb.Message(res[0].dn)
-               machinepass = glue.generate_random_str(12)
-               msg["userPassword"] = ldb.MessageElement(machinepass, ldb.FLAG_MOD_REPLACE, "userPassword")
-               sam_ldb.modify(msg)
-
-               res = sam_ldb.search(expression=("samAccountName=%s$" % names.netbiosname),
-                                    attrs=["msDs-keyVersionNumber"])
-               assert(len(res) == 1)
-               kvno = int(str(res[0]["msDs-keyVersionNumber"]))
-
-               secretsdb_self_join(secrets_ldb, domain=names.domain,
-                                   realm=names.realm,
-                                       domainsid=names.domainsid,
-                                   dnsdomain=names.dnsdomain,
-                                   netbiosname=names.netbiosname,
-                                   machinepass=machinepass,
-                                   key_version_number=kvno,
-                                   secure_channel_type=int(secrets_msg[0]["secureChannelType"][0]))
-               sam_ldb.transaction_prepare_commit()
-               secrets_ldb.transaction_prepare_commit()
-               sam_ldb.transaction_commit()
-               secrets_ldb.transaction_commit()
-       else:
-               secrets_ldb.transaction_cancel()
-
-# From here start the big steps of the program
-# First get files paths
-paths=get_paths(smbconf=smbconf)
-paths.setup = setup_dir
+
+
+def check_for_DNS(refprivate, private):
+    """Check if the provision has already the requirement for dynamic dns
+
+    :param refprivate: The path to the private directory of the reference
+                       provision
+    :param private: The path to the private directory of the upgraded
+                    provision"""
+
+    spnfile = "%s/spn_update_list" % private
+    namedfile = lp.get("dnsupdate:path")
+
+    if not namedfile:
+       namedfile = "%s/named.conf.update" % private
+
+    if not os.path.exists(spnfile):
+        shutil.copy("%s/spn_update_list" % refprivate, "%s" % spnfile)
+
+    destdir = "%s/new_dns" % private
+    dnsdir = "%s/dns" % private
+
+    if not os.path.exists(namedfile):
+        if not os.path.exists(destdir):
+            os.mkdir(destdir)
+        if not os.path.exists(dnsdir):
+            os.mkdir(dnsdir)
+        shutil.copy("%s/named.conf" % refprivate, "%s/named.conf" % destdir)
+        shutil.copy("%s/named.txt" % refprivate, "%s/named.txt" % destdir)
+        message(SIMPLE, "It seems that you provision didn't integrate new rules "
+                "for dynamic dns update of domain related entries")
+        message(SIMPLE, "A copy of the new bind configuration files and "
+                "template as been put in %s, you should read them and configure dynamic "
+                " dns update" % destdir)
+
+
+def populate_links(samdb, schemadn):
+    """Populate an array with all the back linked attributes
+
+    This attributes that are modified automaticaly when
+    front attibutes are changed
+
+    :param samdb: A LDB object for sam.ldb file
+    :param schemadn: DN of the schema for the partition"""
+    linkedAttHash = get_linked_attributes(Dn(samdb, str(schemadn)), samdb)
+    backlinked.extend(linkedAttHash.values())
+    for t in linkedAttHash.keys():
+        forwardlinked.add(t)
+
+
+def populate_dnsyntax(samdb, schemadn):
+    """Populate an array with all the attributes that have DN synthax
+       (oid 2.5.5.1)
+
+    :param samdb: A LDB object for sam.ldb file
+    :param schemadn: DN of the schema for the partition"""
+    res = samdb.search(expression="(attributeSyntax=2.5.5.1)", base=Dn(samdb,
+                        str(schemadn)), scope=SCOPE_SUBTREE,
+                        attrs=["lDAPDisplayName"])
+    for elem in res:
+        dn_syntax_att.append(elem["lDAPDisplayName"])
+
+
+def sanitychecks(samdb, names):
+    """Make some checks before trying to update
+
+    :param samdb: An LDB object opened on sam.ldb
+    :param names: list of key provision parameters
+    :return: Status of check (1 for Ok, 0 for not Ok) """
+    res = samdb.search(expression="objectClass=ntdsdsa", base=str(names.configdn),
+                         scope=SCOPE_SUBTREE, attrs=["dn"],
+                         controls=["search_options:1:2"])
+    if len(res) == 0:
+        print "No DC found, your provision is most probably hardly broken !"
+        return False
+    elif len(res) != 1:
+        print "Found %d domain controllers, for the moment upgradeprovision" \
+              "is not able to handle upgrade on domain with more than one DC, please demote" \
+              " the other(s) DC(s) before upgrading" % len(res)
+        return False
+    else:
+        return True
+
+
+def print_provision_key_parameters(names):
+    """Do a a pretty print of provision parameters
+
+    :param names: list of key provision parameters """
+    message(GUESS, "rootdn      :" + str(names.rootdn))
+    message(GUESS, "configdn    :" + str(names.configdn))
+    message(GUESS, "schemadn    :" + str(names.schemadn))
+    message(GUESS, "serverdn    :" + str(names.serverdn))
+    message(GUESS, "netbiosname :" + names.netbiosname)
+    message(GUESS, "defaultsite :" + names.sitename)
+    message(GUESS, "dnsdomain   :" + names.dnsdomain)
+    message(GUESS, "hostname    :" + names.hostname)
+    message(GUESS, "domain      :" + names.domain)
+    message(GUESS, "realm       :" + names.realm)
+    message(GUESS, "invocationid:" + names.invocation)
+    message(GUESS, "policyguid  :" + names.policyid)
+    message(GUESS, "policyguiddc:" + str(names.policyid_dc))
+    message(GUESS, "domainsid   :" + str(names.domainsid))
+    message(GUESS, "domainguid  :" + names.domainguid)
+    message(GUESS, "ntdsguid    :" + names.ntdsguid)
+    message(GUESS, "domainlevel :" + str(names.domainlevel))
+
+
+def handle_special_case(att, delta, new, old, usn, basedn, aldb):
+    """Define more complicate update rules for some attributes
+
+    :param att: The attribute to be updated
+    :param delta: A messageElement object that correspond to the difference
+                  between the updated object and the reference one
+    :param new: The reference object
+    :param old: The Updated object
+    :param usn: The highest usn modified by a previous (upgrade)provision
+    :param basedn: The base DN of the provision
+    :param aldb: An ldb object used to build DN
+    :return: True to indicate that the attribute should be kept, False for
+             discarding it"""
+
+    flag = delta.get(att).flags()
+    # We do most of the special case handle if we do not have the
+    # highest usn as otherwise the replPropertyMetaData will guide us more
+    # correctly
+    if usn is None:
+        if (att == "sPNMappings" and flag == FLAG_MOD_REPLACE and
+            ldb.Dn(aldb, "CN=Directory Service,CN=Windows NT,"
+                        "CN=Services,CN=Configuration,%s" % basedn)
+                        == old[0].dn):
+            return True
+        if (att == "userAccountControl" and flag == FLAG_MOD_REPLACE and
+            ldb.Dn(aldb, "CN=Administrator,CN=Users,%s" % basedn)
+                        == old[0].dn):
+            message(SIMPLE, "We suggest that you change the userAccountControl"
+                            " for user Administrator from value %d to %d" %
+                            (int(str(old[0][att])), int(str(new[0][att]))))
+            return False
+        if (att == "minPwdAge" and flag == FLAG_MOD_REPLACE):
+            if (long(str(old[0][att])) == 0):
+                delta[att] = MessageElement(new[0][att], FLAG_MOD_REPLACE, att)
+            return True
+
+        if (att == "member" and flag == FLAG_MOD_REPLACE):
+            hash = {}
+            newval = []
+            changeDelta=0
+            for elem in old[0][att]:
+                hash[str(elem).lower()]=1
+                newval.append(str(elem))
+
+            for elem in new[0][att]:
+                if not hash.has_key(str(elem).lower()):
+                    changeDelta=1
+                    newval.append(str(elem))
+            if changeDelta == 1:
+                delta[att] = MessageElement(newval, FLAG_MOD_REPLACE, att)
+            else:
+                delta.remove(att)
+            return True
+
+        if (att in ("gPLink", "gPCFileSysPath") and
+            flag == FLAG_MOD_REPLACE and
+            str(new[0].dn).lower() == str(old[0].dn).lower()):
+            delta.remove(att)
+            return True
+
+        if att == "forceLogoff":
+            ref=0x8000000000000000
+            oldval=int(old[0][att][0])
+            newval=int(new[0][att][0])
+            ref == old and ref == abs(new)
+            return True
+
+        if att in ("adminDisplayName", "adminDescription"):
+            return True
+
+        if (str(old[0].dn) == "CN=Samba4-Local-Domain, %s" % (names.schemadn)
+            and att == "defaultObjectCategory" and flag == FLAG_MOD_REPLACE):
+            return True
+
+        if (str(old[0].dn) == "CN=Title, %s" % (str(names.schemadn)) and
+                att == "rangeUpper" and flag == FLAG_MOD_REPLACE):
+            return True
+
+        if (str(old[0].dn) == "%s" % (str(names.rootdn))
+                and att == "subRefs" and flag == FLAG_MOD_REPLACE):
+            return True
+
+        if str(delta.dn).endswith("CN=DisplaySpecifiers, %s" % names.configdn):
+            return True
+
+    # This is a bit of special animal as we might have added
+    # already SPN entries to the list that has to be modified
+    # So we go in detail to try to find out what has to be added ...
+    if (att == "servicePrincipalName" and flag == FLAG_MOD_REPLACE):
+        hash = {}
+        newval = []
+        changeDelta=0
+        for elem in old[0][att]:
+            hash[str(elem)]=1
+            newval.append(str(elem))
+
+        for elem in new[0][att]:
+            if not hash.has_key(str(elem)):
+                changeDelta=1
+                newval.append(str(elem))
+        if changeDelta == 1:
+            delta[att] = MessageElement(newval, FLAG_MOD_REPLACE, att)
+        else:
+            delta.remove(att)
+        return True
+
+    return False
+
+def dump_denied_change(dn, att, flagtxt, current, reference):
+    """Print detailed information about why a changed is denied
+
+    :param dn: DN of the object which attribute is denied
+    :param att: Attribute that was supposed to be upgraded
+    :param flagtxt: Type of the update that should be performed
+                    (add, change, remove, ...)
+    :param current: Value(s) of the current attribute
+    :param reference: Value(s) of the reference attribute"""
+
+    message(CHANGE, "dn= " + str(dn)+" " + att+" with flag " + flagtxt
+                +" is not allowed to be changed/removed, I discard this change")
+    if att != "objectSid" :
+        i = 0
+        for e in range(0, len(current)):
+            message(CHANGE, "old %d : %s" % (i, str(current[e])))
+            i+=1
+        if reference is not None:
+            i = 0
+            for e in range(0, len(reference)):
+                message(CHANGE, "new %d : %s" % (i, str(reference[e])))
+                i+=1
+    else:
+        message(CHANGE, "old : %s" % ndr_unpack(security.dom_sid, current[0]))
+        message(CHANGE, "new : %s" % ndr_unpack(security.dom_sid, reference[0]))
+
+
+def handle_special_add(samdb, dn, names):
+    """Handle special operation (like remove) on some object needed during
+    upgrade
+
+    This is mostly due to wrong creation of the object in previous provision.
+    :param samdb: An Ldb object representing the SAM database
+    :param dn: DN of the object to inspect
+    :param names: list of key provision parameters
+    """
+
+    dntoremove = None
+    objDn = Dn(samdb, "CN=IIS_IUSRS, CN=Builtin, %s" % names.rootdn)
+    if dn == objDn :
+        #This entry was misplaced lets remove it if it exists
+        dntoremove = "CN=IIS_IUSRS, CN=Users, %s" % names.rootdn
+
+    objDn = Dn(samdb,
+                "CN=Certificate Service DCOM Access, CN=Builtin, %s" % names.rootdn)
+    if dn == objDn:
+        #This entry was misplaced lets remove it if it exists
+        dntoremove = "CN=Certificate Service DCOM Access,"\
+                     "CN=Users, %s" % names.rootdn
+        print dntoremove
+
+    objDn = Dn(samdb, "CN=Cryptographic Operators, CN=Builtin, %s" % names.rootdn)
+    if dn == objDn:
+        #This entry was misplaced lets remove it if it exists
+        dntoremove = "CN=Cryptographic Operators, CN=Users, %s" % names.rootdn
+
+    objDn = Dn(samdb, "CN=Event Log Readers, CN=Builtin, %s" % names.rootdn)
+    if dn == objDn:
+        #This entry was misplaced lets remove it if it exists
+        dntoremove = "CN=Event Log Readers, CN=Users, %s" % names.rootdn
+
+    objDn = Dn(samdb,"CN=System,CN=WellKnown Security Principals,"
+                     "CN=Configuration,%s" % names.rootdn)
+    if dn == objDn:
+        oldDn = Dn(samdb,"CN=Well-Known-Security-Id-System,"
+                         "CN=WellKnown Security Principals,"
+                         "CN=Configuration,%s" % names.rootdn)
+
+        res = samdb.search(expression="(dn=%s)" % oldDn,
+                            base=str(names.rootdn),
+                            scope=SCOPE_SUBTREE, attrs=["dn"],
+                            controls=["search_options:1:2"])
+        if len(res) > 0:
+            message(CHANGE, "Existing object %s must be replaced by %s,"
+                            "Renaming old object" % (str(oldDn), str(dn)))
+            samdb.rename(oldDn, objDn)
+
+        return 1
+
+    if dntoremove is not None:
+        res = samdb.search(expression="(dn=%s)" % dntoremove,
+                            base=str(names.rootdn),
+                            scope=SCOPE_SUBTREE, attrs=["dn"],
+                            controls=["search_options:1:2"])
+        if len(res) > 0:
+            message(CHANGE, "Existing object %s must be replaced by %s,"
+                            "removing old object" % (dntoremove, str(dn)))
+            samdb.delete(res[0]["dn"])
+    return 0
+
+
+def check_dn_nottobecreated(hash, index, listdn):
+    """Check if one of the DN present in the list has a creation order
+       greater than the current.
+
+    Hash is indexed by dn to be created, with each key
+    is associated the creation order.
+
+    First dn to be created has the creation order 0, second has 1, ...
+    Index contain the current creation order
+
+    :param hash: Hash holding the different DN of the object to be
+                  created as key
+    :param index: Current creation order
+    :param listdn: List of DNs on which the current DN depends on
+    :return: None if the current object do not depend on other
+              object or if all object have been created before."""
+    if listdn is None:
+        return None
+    for dn in listdn:
+        key = str(dn).lower()
+        if hash.has_key(key) and hash[key] > index:
+            return str(dn)
+    return None
+
+
+
+def add_missing_object(ref_samdb, samdb, dn, names, basedn, hash, index):
+    """Add a new object if the dependencies are satisfied
+
+    The function add the object if the object on which it depends are already
+    created
+
+    :param ref_samdb: Ldb object representing the SAM db of the reference
+                       provision
+    :param samdb: Ldb object representing the SAM db of the upgraded
+                   provision
+    :param dn: DN of the object to be added
+    :param names: List of key provision parameters
+    :param basedn: DN of the partition to be updated
+    :param hash: Hash holding the different DN of the object to be
+                  created as key
+    :param index: Current creation order
+    :return: True if the object was created False otherwise"""
+
+    if handle_special_add(samdb, dn, names):
+        return
+    reference = ref_samdb.search(expression="dn=%s" % (str(dn)), base=basedn,
+                    scope=SCOPE_SUBTREE, controls=["search_options:1:2"])
+    empty = Message()
+    delta = samdb.msg_diff(empty, reference[0])
+    delta.dn
+    skip = False
+    try:
+        if str(reference[0].get("cn"))  == "RID Set":
+            for klass in reference[0].get("objectClass"):
+                if str(klass).lower == "ridset":
+                    skip = True
+    finally:
+        if delta.get("objectSid"):
+            sid = str(ndr_unpack(security.dom_sid, str(reference[0]["objectSid"])))
+            m = re.match(r".*-(\d+)$", sid)
+            if m and int(m.group(1))>999:
+                delta.remove("objectSid")
+        for att in hashAttrNotCopied.keys():
+            delta.remove(att)
+        for att in backlinked:
+            delta.remove(att)
+        depend_on_yettobecreated = None
+        for att in dn_syntax_att:
+            depend_on_yet_tobecreated = check_dn_nottobecreated(hash, index,
+                                                                delta.get(str(att)))
+            if depend_on_yet_tobecreated is not None:
+                message(CHANGE, "Object %s depends on %s in attribute %s,"
+                                "delaying the creation" % (dn,
+                                          depend_on_yet_tobecreated, att))
+                return False
+
+        delta.dn = dn
+        if not skip:
+            message(CHANGE,"Object %s will be added" % dn)
+            samdb.add(delta, ["relax:0"])
+        else:
+            message(CHANGE,"Object %s was skipped" % dn)
+
+        return True
+
+def gen_dn_index_hash(listMissing):
+    """Generate a hash associating the DN to its creation order
+
+    :param listMissing: List of DN
+    :return: Hash with DN as keys and creation order as values"""
+    hash = {}
+    for i in range(0, len(listMissing)):
+        hash[str(listMissing[i]).lower()] = i
+    return hash
+
+def add_deletedobj_containers(ref_samdb, samdb, names):
+    """Add the object containter: CN=Deleted Objects
+
+    This function create the container for each partition that need one and
+    then reference the object into the root of the partition
+
+    :param ref_samdb: Ldb object representing the SAM db of the reference
+                       provision
+    :param samdb: Ldb object representing the SAM db of the upgraded provision
+    :param names: List of key provision parameters"""
+
+
+    wkoPrefix = "B:32:18E2EA80684F11D2B9AA00C04F79F805"
+    partitions = [str(names.rootdn), str(names.configdn)]
+    for part in partitions:
+        ref_delObjCnt = ref_samdb.search(expression="(cn=Deleted Objects)",
+                                            base=part, scope=SCOPE_SUBTREE,
+                                            attrs=["dn"],
+                                            controls=["show_deleted:0"])
+        delObjCnt = samdb.search(expression="(cn=Deleted Objects)",
+                                    base=part, scope=SCOPE_SUBTREE,
+                                    attrs=["dn"],
+                                    controls=["show_deleted:0"])
+        if len(ref_delObjCnt) > len(delObjCnt):
+            reference = ref_samdb.search(expression="cn=Deleted Objects",
+                                            base=part, scope=SCOPE_SUBTREE,
+                                            controls=["show_deleted:0"])
+            empty = Message()
+            delta = samdb.msg_diff(empty, reference[0])
+
+            delta.dn = Dn(samdb, str(reference[0]["dn"]))
+            for att in hashAttrNotCopied.keys():
+                delta.remove(att)
+            samdb.add(delta)
+
+            listwko = []
+            res = samdb.search(expression="(objectClass=*)", base=part,
+                               scope=SCOPE_BASE,
+                               attrs=["dn", "wellKnownObjects"])
+
+            targetWKO = "%s:%s" % (wkoPrefix, str(reference[0]["dn"]))
+            found = False
+
+            if len(res[0]) > 0:
+                wko = res[0]["wellKnownObjects"]
+
+                # The wellKnownObject that we want to add.
+                for o in wko:
+                    if str(o) == targetWKO:
+                        found = True
+                    listwko.append(str(o))
+
+            if not found:
+                listwko.append(targetWKO)
+
+                delta = Message()
+                delta.dn = Dn(samdb, str(res[0]["dn"]))
+                delta["wellKnownObjects"] = MessageElement(listwko,
+                                                FLAG_MOD_REPLACE,
+                                                "wellKnownObjects" )
+                samdb.modify(delta)
+
+def add_missing_entries(ref_samdb, samdb, names, basedn, list):
+    """Add the missing object whose DN is the list
+
+    The function add the object if the objects on which it depends are
+    already created.
+
+    :param ref_samdb: Ldb object representing the SAM db of the reference
+                      provision
+    :param samdb: Ldb object representing the SAM db of the upgraded
+                  provision
+    :param dn: DN of the object to be added
+    :param names: List of key provision parameters
+    :param basedn: DN of the partition to be updated
+    :param list: List of DN to be added in the upgraded provision"""
+
+    listMissing = []
+    listDefered = list
+
+    while(len(listDefered) != len(listMissing) and len(listDefered) > 0):
+        index = 0
+        listMissing = listDefered
+        listDefered = []
+        hashMissing = gen_dn_index_hash(listMissing)
+        for dn in listMissing:
+            ret = add_missing_object(ref_samdb, samdb, dn, names, basedn,
+                                        hashMissing, index)
+            index = index + 1
+            if ret == 0:
+                # DN can't be created because it depends on some
+                # other DN in the list
+                listDefered.append(dn)
+    if len(listDefered) != 0:
+        raise ProvisioningError("Unable to insert missing elements:" \
+                                "circular references")
+
+def handle_links(samdb, att, basedn, dn, value, ref_value, delta):
+    """This function handle updates on links
+
+    :param samdb: An LDB object pointing to the updated provision
+    :param att: Attribute to update
+    :param basedn: The root DN of the provision
+    :param dn: The DN of the inspected object
+    :param value: The value of the attribute
+    :param ref_value: The value of this attribute in the reference provision
+    :param delta: The MessageElement object that will be applied for
+                   transforming the current provision"""
+
+    res = samdb.search(expression="dn=%s" % dn, base=basedn,
+                        controls=["search_options:1:2", "reveal:1"],
+                        attrs=[att])
+
+    blacklist = {}
+    hash = {}
+    newlinklist = []
+    changed = False
+
+    newlinklist.extend(value)
+
+    for e in value:
+        hash[e] = 1
+    # for w2k domain level the reveal won't reveal anything ...
+    # it means that we can readd links that were removed on purpose ...
+    # Also this function in fact just accept add not removal
+
+    for e in res[0][att]:
+        if not hash.has_key(e):
+            # We put in the blacklist all the element that are in the "revealed"
+            # result and not in the "standard" result
+            # This element are links that were removed before and so that
+            # we don't wan't to readd
+            blacklist[e] = 1
+
+    for e in ref_value:
+        if not blacklist.has_key(e) and not hash.has_key(e):
+            newlinklist.append(str(e))
+            changed = True
+    if changed:
+        delta[att] = MessageElement(newlinklist, FLAG_MOD_REPLACE, att)
+    else:
+        delta.remove(att)
+
+
+msg_elt_flag_strs = {
+    ldb.FLAG_MOD_ADD: "MOD_ADD",
+    ldb.FLAG_MOD_REPLACE: "MOD_REPLACE",
+    ldb.FLAG_MOD_DELETE: "MOD_DELETE" }
+
+
+def update_present(ref_samdb, samdb, basedn, listPresent, usns, invocationid):
+    """ This function updates the object that are already present in the
+        provision
+
+    :param ref_samdb: An LDB object pointing to the reference provision
+    :param samdb: An LDB object pointing to the updated provision
+    :param basedn: A string with the value of the base DN for the provision
+                   (ie. DC=foo, DC=bar)
+    :param listPresent: A list of object that is present in the provision
+    :param usns: A list of USN range modified by previous provision and
+                 upgradeprovision
+    :param invocationid: The value of the invocationid for the current DC"""
+
+    global defSDmodified
+    # This hash is meant to speedup lookup of attribute name from an oid,
+    # it's for the replPropertyMetaData handling
+    hash_oid_name = {}
+    res = samdb.search(expression="objectClass=attributeSchema", base=basedn,
+                        controls=["search_options:1:2"], attrs=["attributeID",
+                        "lDAPDisplayName"])
+    if len(res) > 0:
+        for e in res:
+            strDisplay = str(e.get("lDAPDisplayName"))
+            hash_oid_name[str(e.get("attributeID"))] = strDisplay
+    else:
+        msg = "Unable to insert missing elements: circular references"
+        raise ProvisioningError(msg)
+
+    changed = 0
+    controls = ["search_options:1:2", "sd_flags:1:2"]
+    for dn in listPresent:
+        reference = ref_samdb.search(expression="dn=%s" % (str(dn)), base=basedn,
+                                        scope=SCOPE_SUBTREE,
+                                        controls=controls)
+        current = samdb.search(expression="dn=%s" % (str(dn)), base=basedn,
+                                scope=SCOPE_SUBTREE, controls=controls)
+
+        if (
+             (str(current[0].dn) != str(reference[0].dn)) and
+             (str(current[0].dn).upper() == str(reference[0].dn).upper())
+           ):
+            message(CHANGE, "Name are the same but case change,"\
+                            "let's rename %s to %s" % (str(current[0].dn),
+                                                       str(reference[0].dn)))
+            identic_rename(samdb, reference[0].dn)
+            current = samdb.search(expression="dn=%s" % (str(dn)), base=basedn,
+                                    scope=SCOPE_SUBTREE,
+                                    controls=["search_options:1:2"])
+
+        delta = samdb.msg_diff(current[0], reference[0])
+
+        for att in hashAttrNotCopied.keys():
+            delta.remove(att)
+
+        for att in backlinked:
+            delta.remove(att)
+
+        delta.remove("name")
+
+        if len(delta.items()) > 1 and usns is not None:
+            # Fetch the replPropertyMetaData
+            res = samdb.search(expression="dn=%s" % (str(dn)), base=basedn,
+                                scope=SCOPE_SUBTREE, controls=controls,
+                                attrs=["replPropertyMetaData"])
+            ctr = ndr_unpack(drsblobs.replPropertyMetaDataBlob,
+                                str(res[0]["replPropertyMetaData"])).ctr
+
+            hash_attr_usn = {}
+            for o in ctr.array:
+                # We put in this hash only modification
+                # made on the current host
+                att = hash_oid_name[samdb.get_oid_from_attid(o.attid)]
+                if str(o.originating_invocation_id) == str(invocationid):
+                # Note we could just use 1 here
+                    hash_attr_usn[att] = o.originating_usn
+                else:
+                    hash_attr_usn[att] = -1
+
+        isFirst = 0
+        txt = ""
+
+        for att in delta:
+            if usns is not None:
+                # We have updated by provision usn information so let's exploit
+                # replMetadataProperties
+                if att in forwardlinked:
+                    handle_links(samdb, att, basedn, current[0]["dn"],
+                                    current[0][att], reference[0][att], delta)
+
+                if isFirst == 0 and len(delta.items())>1:
+                    isFirst = 1
+                    txt = "%s\n" % (str(dn))
+                if att == "dn":
+                    # There is always a dn attribute after a msg_diff
+                    continue
+                if att == "rIDAvailablePool":
+                    delta.remove(att)
+                    continue
+                if att == "objectSid":
+                    delta.remove(att)
+                    continue
+                if att == "creationTime":
+                    delta.remove(att)
+                    continue
+                if att == "oEMInformation":
+                    delta.remove(att)
+                    continue
+                if att == "msDs-KeyVersionNumber":
+                # This is the kvno of the computer/user it's a very bad
+                # idea to change it
+                    delta.remove(att)
+                    continue
+                if handle_special_case(att, delta, reference, current, usns, basedn, samdb):
+                    # This attribute is "complicated" to handle and handling
+                    # was done in handle_special_case
+                    continue
+                attrUSN = hash_attr_usn.get(att)
+                if att == "forceLogoff" and attrUSN is None:
+                    continue
+                if  attrUSN is None:
+                    delta.remove(att)
+                    continue
+
+                if attrUSN == -1:
+                    # This attribute was last modified by another DC forget
+                    # about it
+                    message(CHANGE, "%sAttribute: %s has been"
+                            "created/modified/deleted  by another DC,"
+                            " do nothing" % (txt, att ))
+                    txt = ""
+                    delta.remove(att)
+                    continue
+                elif not usn_in_range(int(attrUSN), usns):
+                    message(CHANGE, "%sAttribute: %s has been"
+                                    "created/modified/deleted not during a"
+                                    " provision or upgradeprovision: current"
+                                    " usn %d , do nothing" % (txt, att, attrUSN))
+                    txt = ""
+                    delta.remove(att)
+                    continue
+                else:
+                    if att == "defaultSecurityDescriptor":
+                        defSDmodified = True
+                    if attrUSN:
+                        message(CHANGE, "%sAttribute: %s will be modified"
+                                        "/deleted it was last modified"
+                                        "during a provision, current usn:"
+                                        "%d" % (txt, att,  attrUSN))
+                        txt = ""
+                    else:
+                        message(CHANGE, "%sAttribute: %s will be added because"
+                                        " it hasn't existed before " % (txt, att))
+                        txt = ""
+                    continue
+
+            else:
+            # Old school way of handling things for pre alpha12 upgrade
+                defSDmodified = True
+                msgElt = delta.get(att)
+
+                if att == "nTSecurityDescriptor":
+                    delta.remove(att)
+                    continue
+
+                if att == "dn":
+                    continue
+
+                if not hashOverwrittenAtt.has_key(att):
+                    if msgElt.flags() != FLAG_MOD_ADD:
+                        if not handle_special_case(att, delta, reference, current,
+                                                    usns, basedn, samdb):
+                            if opts.debugchange or opts.debugall:
+                                try:
+                                    dump_denied_change(dn, att,
+                                        msg_elt_flag_strs[msgElt.flags()],
+                                        current[0][att], reference[0][att])
+                                except KeyError:
+                                    dump_denied_change(dn, att,
+                                        msg_elt_flag_strs[msgElt.flags()],
+                                        current[0][att], None)
+                            delta.remove(att)
+                        continue
+                else:
+                    if hashOverwrittenAtt.get(att)&2**msgElt.flags() :
+                        continue
+                    elif  hashOverwrittenAtt.get(att)==never:
+                        delta.remove(att)
+                        continue
+
+        delta.dn = dn
+        if len(delta.items()) >1:
+            attributes=", ".join(delta.keys())
+            message(CHANGE, "%s is different from the reference one, changed"
+                            " attributes: %s\n" % (dn, attributes))
+            changed += 1
+            samdb.modify(delta)
+    return changed
+
+def reload_full_schema(samdb, names):
+    """Load the updated schema with all the new and existing classes
+       and attributes.
+
+    :param samdb: An LDB object connected to the sam.ldb of the update
+                  provision
+    :param names: List of key provision parameters
+    """
+
+    current = samdb.search(expression="objectClass=*", base=str(names.schemadn),
+                                scope=SCOPE_SUBTREE)
+    schema_ldif = ""
+    prefixmap_data = ""
+
+    for ent in current:
+        schema_ldif = "%s%s" % (schema_ldif,
+                        samdb.write_ldif(ent, ldb.CHANGETYPE_NONE))
+
+    prefixmap_data = open(setup_path("prefixMap.txt"), 'r').read()
+    prefixmap_data = b64encode(prefixmap_data)
+
+    # We don't actually add this ldif, just parse it
+    prefixmap_ldif = "dn: cn=schema\nprefixMap:: %s\n\n" % prefixmap_data
+
+    dsdb._dsdb_set_schema_from_ldif(samdb, prefixmap_ldif, schema_ldif)
+
+
+def update_partition(ref_samdb, samdb, basedn, names, schema, provisionUSNs):
+    """Check differences between the reference provision and the upgraded one.
+
+    It looks for all objects which base DN is name.
+
+    This function will also add the missing object and update existing object
+    to add or remove attributes that were missing.
+
+    :param ref_sambdb: An LDB object conntected to the sam.ldb of the
+                       reference provision
+    :param samdb: An LDB object connected to the sam.ldb of the update
+                  provision
+    :param basedn: String value of the DN of the partition
+    :param names: List of key provision parameters
+    :param schema: A Schema object
+    :param provisionUSNs:  The USNs modified by provision/upgradeprovision
+                           last time"""
+
+    hash_new = {}
+    hash = {}
+    listMissing = []
+    listPresent = []
+    reference = []
+    current = []
+
+    # Connect to the reference provision and get all the attribute in the
+    # partition referred by name
+    reference = ref_samdb.search(expression="objectClass=*", base=basedn,
+                                    scope=SCOPE_SUBTREE, attrs=["dn"],
+                                    controls=["search_options:1:2"])
+
+    current = samdb.search(expression="objectClass=*", base=basedn,
+                                scope=SCOPE_SUBTREE, attrs=["dn"],
+                                controls=["search_options:1:2"])
+    # Create a hash for speeding the search of new object
+    for i in range(0, len(reference)):
+        hash_new[str(reference[i]["dn"]).lower()] = reference[i]["dn"]
+
+    # Create a hash for speeding the search of existing object in the
+    # current provision
+    for i in range(0, len(current)):
+        hash[str(current[i]["dn"]).lower()] = current[i]["dn"]
+
+
+    for k in hash_new.keys():
+        if not hash.has_key(k):
+            if not str(hash_new[k]) == "CN=Deleted Objects, %s" % names.rootdn:
+                listMissing.append(hash_new[k])
+        else:
+            listPresent.append(hash_new[k])
+
+    # Sort the missing object in order to have object of the lowest level
+    # first (which can be containers for higher level objects)
+    listMissing.sort(dn_sort)
+    listPresent.sort(dn_sort)
+
+    # The following lines is to load the up to
+    # date schema into our current LDB
+    # a complete schema is needed as the insertion of attributes
+    # and class is done against it
+    # and the schema is self validated
+    samdb.set_schema(schema)
+    try:
+        message(SIMPLE, "There are %d missing objects" % (len(listMissing)))
+        add_deletedobj_containers(ref_samdb, samdb, names)
+
+        add_missing_entries(ref_samdb, samdb, names, basedn, listMissing)
+
+        reload_full_schema(samdb, names)
+
+        changed = update_present(ref_samdb, samdb, basedn, listPresent,
+                                    provisionUSNs, names.invocation)
+        message(SIMPLE, "There are %d changed objects" % (changed))
+        return 1
+
+    except StandardError, err:
+        message(ERROR, "Exception during upgrade of samdb:")
+        (typ, val, tb) = sys.exc_info()
+        traceback.print_exception(typ, val, tb)
+        return 0
+
+
+def check_updated_sd(ref_sam, cur_sam, names):
+    """Check if the security descriptor in the upgraded provision are the same
+       as the reference
+
+    :param ref_sam: A LDB object connected to the sam.ldb file used as
+                    the reference provision
+    :param cur_sam: A LDB object connected to the sam.ldb file used as
+                    upgraded provision
+    :param names: List of key provision parameters"""
+    reference = ref_sam.search(expression="objectClass=*", base=str(names.rootdn),
+                                scope=SCOPE_SUBTREE,
+                                attrs=["dn", "nTSecurityDescriptor"],
+                                controls=["search_options:1:2"])
+    current = cur_sam.search(expression="objectClass=*", base=str(names.rootdn),
+                                scope=SCOPE_SUBTREE,
+                                attrs=["dn", "nTSecurityDescriptor"],
+                                controls=["search_options:1:2"])
+    hash = {}
+    for i in range(0, len(reference)):
+        refsd = ndr_unpack(security.descriptor,
+                    str(reference[i]["nTSecurityDescriptor"]))
+        hash[str(reference[i]["dn"]).lower()] = refsd.as_sddl(names.domainsid)
+
+
+    for i in range(0, len(current)):
+        key = str(current[i]["dn"]).lower()
+        if hash.has_key(key):
+            cursd = ndr_unpack(security.descriptor,
+                        str(current[i]["nTSecurityDescriptor"]))
+            sddl = cursd.as_sddl(names.domainsid)
+            if sddl != hash[key]:
+                txt = get_diff_sddls(hash[key], sddl)
+                if txt != "":
+                    message(CHANGESD, "On object %s ACL is different"
+                                      " \n%s" % (current[i]["dn"], txt))
+
+
+
+def fix_partition_sd(samdb, names):
+    """This function fix the SD for partition containers (basedn, configdn, ...)
+    This is needed because some provision use to have broken SD on containers
+
+    :param samdb: An LDB object pointing to the sam of the current provision
+    :param names: A list of key provision parameters
+    """
+    # First update the SD for the rootdn
+    res = samdb.search(expression="objectClass=*", base=str(names.rootdn),
+                         scope=SCOPE_BASE, attrs=["dn", "whenCreated"],
+                         controls=["search_options:1:2"])
+    delta = Message()
+    delta.dn = Dn(samdb, str(res[0]["dn"]))
+    descr = get_domain_descriptor(names.domainsid)
+    delta["nTSecurityDescriptor"] = MessageElement(descr, FLAG_MOD_REPLACE,
+                                                    "nTSecurityDescriptor")
+    samdb.modify(delta, ["recalculate_sd:0"])
+    # Then the config dn
+    res = samdb.search(expression="objectClass=*", base=str(names.configdn),
+                        scope=SCOPE_BASE, attrs=["dn", "whenCreated"],
+                        controls=["search_options:1:2"])
+    delta = Message()
+    delta.dn = Dn(samdb, str(res[0]["dn"]))
+    descr = get_config_descriptor(names.domainsid)
+    delta["nTSecurityDescriptor"] = MessageElement(descr, FLAG_MOD_REPLACE,
+                                                    "nTSecurityDescriptor" )
+    samdb.modify(delta, ["recalculate_sd:0"])
+    # Then the schema dn
+    res = samdb.search(expression="objectClass=*", base=str(names.schemadn),
+                        scope=SCOPE_BASE, attrs=["dn", "whenCreated"],
+                        controls=["search_options:1:2"])
+
+    delta = Message()
+    delta.dn = Dn(samdb, str(res[0]["dn"]))
+    descr = get_schema_descriptor(names.domainsid)
+    delta["nTSecurityDescriptor"] = MessageElement(descr, FLAG_MOD_REPLACE,
+                                                    "nTSecurityDescriptor" )
+    samdb.modify(delta, ["recalculate_sd:0"])
+
+def rebuild_sd(samdb, names):
+    """Rebuild security descriptor of the current provision from scratch
+
+    During the different pre release of samba4 security descriptors (SD)
+    were notarly broken (up to alpha11 included)
+    This function allow to get them back in order, this function make the
+    assumption that nobody has modified manualy an SD
+    and so SD can be safely recalculated from scratch to get them right.
+
+    :param names: List of key provision parameters"""
+
+
+    hash = {}
+    res = samdb.search(expression="objectClass=*", base=str(names.rootdn),
+                        scope=SCOPE_SUBTREE, attrs=["dn", "whenCreated"],
+                        controls=["search_options:1:2"])
+    for obj in res:
+        if not (str(obj["dn"]) == str(names.rootdn) or
+            str(obj["dn"]) == str(names.configdn) or
+            str(obj["dn"]) == str(names.schemadn)):
+            hash[str(obj["dn"])] = obj["whenCreated"]
+
+    listkeys = hash.keys()
+    listkeys.sort(dn_sort)
+
+    for key in listkeys:
+        try:
+            delta = Message()
+            delta.dn = Dn(samdb, key)
+            delta["whenCreated"] = MessageElement(hash[key], FLAG_MOD_REPLACE,
+                                                    "whenCreated" )
+            samdb.modify(delta, ["recalculate_sd:0"])
+        except:
+            # XXX: We should always catch an explicit exception.
+            # What could go wrong here?
+            samdb.transaction_cancel()
+            res = samdb.search(expression="objectClass=*", base=str(names.rootdn),
+                                scope=SCOPE_SUBTREE,
+                                attrs=["dn", "nTSecurityDescriptor"],
+                                controls=["search_options:1:2"])
+            badsd = ndr_unpack(security.descriptor,
+                        str(res[0]["nTSecurityDescriptor"]))
+            print "bad stuff %s" % badsd.as_sddl(names.domainsid)
+            return
+
+def removeProvisionUSN(samdb):
+        attrs = [samba.provision.LAST_PROVISION_USN_ATTRIBUTE, "dn"]
+        entry = samdb.search(expression="dn=@PROVISION", base = "",
+                                scope=SCOPE_SUBTREE,
+                                controls=["search_options:1:2"],
+                                attrs=attrs)
+        empty = Message()
+        empty.dn = entry[0].dn
+        delta = samdb.msg_diff(entry[0], empty)
+        delta.remove("dn")
+        delta.dn = entry[0].dn
+        samdb.modify(delta)
+
+def remove_stored_generated_attrs(paths, creds, session, lp):
+    """Remove previously stored constructed attributes
+
+    :param paths: List of paths for different provision objects
+                        from the upgraded provision
+    :param creds: A credential object
+    :param session: A session object
+    :param lp: A line parser object
+    :return: An associative array whose key are the different constructed
+             attributes and the value the dn where this attributes were found.
+     """
+
+
+def simple_update_basesamdb(newpaths, paths, names):
+    """Update the provision container db: sam.ldb
+    This function is aimed at very old provision (before alpha9)
+
+    :param newpaths: List of paths for different provision objects
+                        from the reference provision
+    :param paths: List of paths for different provision objects
+                        from the upgraded provision
+    :param names: List of key provision parameters"""
+
+    message(SIMPLE, "Copy samdb")
+    shutil.copy(newpaths.samdb, paths.samdb)
+
+    message(SIMPLE, "Update partitions filename if needed")
+    schemaldb = os.path.join(paths.private_dir, "schema.ldb")
+    configldb = os.path.join(paths.private_dir, "configuration.ldb")
+    usersldb = os.path.join(paths.private_dir, "users.ldb")
+    samldbdir = os.path.join(paths.private_dir, "sam.ldb.d")
+
+    if not os.path.isdir(samldbdir):
+        os.mkdir(samldbdir)
+        os.chmod(samldbdir, 0700)
+    if os.path.isfile(schemaldb):
+        shutil.copy(schemaldb, os.path.join(samldbdir,
+                                            "%s.ldb"%str(names.schemadn).upper()))
+        os.remove(schemaldb)
+    if os.path.isfile(usersldb):
+        shutil.copy(usersldb, os.path.join(samldbdir,
+                                            "%s.ldb"%str(names.rootdn).upper()))
+        os.remove(usersldb)
+    if os.path.isfile(configldb):
+        shutil.copy(configldb, os.path.join(samldbdir,
+                                            "%s.ldb"%str(names.configdn).upper()))
+        os.remove(configldb)
+
+
+def update_privilege(ref_private_path, cur_private_path):
+    """Update the privilege database
+
+    :param ref_private_path: Path to the private directory of the reference
+                             provision.
+    :param cur_private_path: Path to the private directory of the current
+                             (and to be updated) provision."""
+    message(SIMPLE, "Copy privilege")
+    shutil.copy(os.path.join(ref_private_path, "privilege.ldb"),
+                os.path.join(cur_private_path, "privilege.ldb"))
+
+
+def update_samdb(ref_samdb, samdb, names, highestUSN, schema):
+    """Upgrade the SAM DB contents for all the provision partitions
+
+    :param ref_sambdb: An LDB object conntected to the sam.ldb of the reference
+                       provision
+    :param samdb: An LDB object connected to the sam.ldb of the update
+                  provision
+    :param names: List of key provision parameters
+    :param highestUSN:  The highest USN modified by provision/upgradeprovision
+                        last time
+    :param schema: A Schema object that represent the schema of the provision"""
+
+    message(SIMPLE, "Starting update of samdb")
+    ret = update_partition(ref_samdb, samdb, str(names.rootdn), names,
+                            schema, highestUSN)
+    if ret:
+        message(SIMPLE, "Update of samdb finished")
+        return 1
+    else:
+        message(SIMPLE, "Update failed")
+        return 0
+
+
+def copyxattrs(dir, refdir):
+    """ Copy owner, groups, extended ACL and NT acls from
+    a reference dir to a destination dir
+
+    Both dir are supposed to hold the same files
+    :param dir: Destination dir
+    :param refdir: Reference directory"""
+
+    noxattr = 0
+    for root, dirs, files in os.walk(dir, topdown=True):
+        for name in files:
+            subdir=root[len(dir):]
+            ref = os.path.join("%s%s" % (refdir, subdir), name)
+            statsinfo = os.stat(ref)
+            tgt = os.path.join(root, name)
+            try:
+
+                os.chown(tgt, statsinfo.st_uid, statsinfo.st_gid)
+                # Get the xattr attributes if any
+                try:
+                    attribute = samba.xattr_native.wrap_getxattr(ref,
+                                                 xattr.XATTR_NTACL_NAME)
+                    samba.xattr_native.wrap_setxattr(tgt,
+                                                 xattr.XATTR_NTACL_NAME,
+                                                 attribute)
+                except:
+                    noxattr = 1
+                attribute = samba.xattr_native.wrap_getxattr(ref,
+                                                 "system.posix_acl_access")
+                samba.xattr_native.wrap_setxattr(tgt,
+                                                 "system.posix_acl_access",
+                                                  attribute)
+            except:
+                continue
+        for name in dirs:
+            subdir=root[len(dir):]
+            ref = os.path.join("%s%s" % (refdir, subdir), name)
+            statsinfo = os.stat(ref)
+            tgt = os.path.join(root, name)
+            try:
+                os.chown(os.path.join(root, name), statsinfo.st_uid,
+                          statsinfo.st_gid)
+                try:
+                    attribute = samba.xattr_native.wrap_getxattr(ref,
+                                                 xattr.XATTR_NTACL_NAME)
+                    samba.xattr_native.wrap_setxattr(tgt,
+                                                 xattr.XATTR_NTACL_NAME,
+                                                 attribute)
+                except:
+                    noxattr = 1
+                attribute = samba.xattr_native.wrap_getxattr(ref,
+                                                 "system.posix_acl_access")
+                samba.xattr_native.wrap_setxattr(tgt,
+                                                 "system.posix_acl_access",
+                                                  attribute)
+
+            except:
+                continue
+
+
+def backup_provision(paths, dir):
+    """This function backup the provision files so that a rollback
+    is possible
+
+    :param paths: Paths to different objects
+    :param dir: Directory where to store the backup
+    """
+
+    shutil.copytree(paths.sysvol, os.path.join(dir, "sysvol"))
+    copyxattrs(os.path.join(dir, "sysvol"), paths.sysvol)
+    shutil.copy2(paths.samdb, dir)
+    shutil.copy2(paths.secrets, dir)
+    shutil.copy2(paths.idmapdb, dir)
+    shutil.copy2(paths.privilege, dir)
+    if os.path.isfile(os.path.join(paths.private_dir,"eadb.tdb")):
+        shutil.copy2(os.path.join(paths.private_dir,"eadb.tdb"), dir)
+    shutil.copy2(paths.smbconf, dir)
+    shutil.copy2(os.path.join(paths.private_dir,"secrets.keytab"), dir)
+
+    samldbdir = os.path.join(paths.private_dir, "sam.ldb.d")
+    if not os.path.isdir(samldbdir):
+        samldbdir = paths.private_dir
+        schemaldb = os.path.join(paths.private_dir, "schema.ldb")
+        configldb = os.path.join(paths.private_dir, "configuration.ldb")
+        usersldb = os.path.join(paths.private_dir, "users.ldb")
+        shutil.copy2(schemaldb, dir)
+        shutil.copy2(usersldb, dir)
+        shutil.copy2(configldb, dir)
+    else:
+        shutil.copytree(samldbdir, os.path.join(dir, "sam.ldb.d"))
+
+
+
+
+def sync_calculated_attributes(samdb, names):
+   """Synchronize attributes used for constructed ones, with the
+      old constructed that were stored in the database.
+
+      This apply for instance to msds-keyversionnumber that was
+      stored and that is now constructed from replpropertymetadata.
+
+      :param samdb: An LDB object attached to the currently upgraded samdb
+      :param names: Various key parameter about current provision.
+   """
+   listAttrs = ["msDs-KeyVersionAttribute"]
+   hash = search_constructed_attrs_stored(samdb, names.rootdn, listAttrs)
+   increment_calculated_keyversion_number(samdb, names.rootdn, hash)
+
 def setup_path(file):
-       return os.path.join(setup_dir, file)
-# Guess all the needed names (variables in fact) from the current
-# provision.
-names = guess_names_from_current_provision(creds,session,paths)
-# Let's see them
-print_names(names)
-# With all this information let's create a fresh new provision used as reference
-provisiondir = newprovision(names,setup_dir,creds,session,smbconf)
-# Get file paths of this new provision
-newpaths = get_paths(targetdir=provisiondir)
-populate_backlink(newpaths,creds,session,names.schemadn)
-# Check the difference
-update_basesamdb(newpaths,paths,names)
-
-if opts.full:
-       update_samdb(newpaths,paths,creds,session,names)
-update_secrets(newpaths,paths,creds,session)
-update_privilege(newpaths,paths)
-update_machine_account_password(paths,creds,session,names)
-# SD should be created with admin but as some previous acl were so wrong that admin can't modify them we have first
-# to recreate them with the good form but with system account and then give the ownership to admin ...
-admin_session_info = admin_session(lp, str(names.domainsid))
-message(SIMPLE,"Updating SD")
-update_sd(paths,creds,session,names)
-update_sd(paths,creds,admin_session_info,names)
-check_updated_sd(newpaths,paths,creds,session,names)
-message(SIMPLE,"Upgrade finished !")
-# remove reference provision now that everything is done !
-rmall(provisiondir)
+    return os.path.join(setup_dir, file)
+
+# Synopsis for updateprovision
+# 1) get path related to provision to be update (called current)
+# 2) open current provision ldbs
+# 3) fetch the key provision parameter (domain sid, domain guid, invocationid
+#    of the DC ....)
+# 4) research of lastProvisionUSN in order to get ranges of USN modified
+#    by either upgradeprovision or provision
+# 5) creation of a new provision the latest version of provision script
+#    (called reference)
+# 6) get reference provision paths
+# 7) open reference provision ldbs
+# 8) setup helpers data that will help the update process
+# 9) update the privilege ldb by copying the one of referecence provision to
+#    the current provision
+# 10)get the oemInfo field, this field contains information about the different
+#    provision that have been done
+# 11)Depending  on whether oemInfo has the string "alpha9" or alphaxx (x as an
+#    integer) or none of this the following things are done
+#    A) When alpha9 or alphaxx is present
+#       The base sam.ldb file is updated by looking at the difference between
+#       referrence one and the current one. Everything is copied with the
+#       exception of lastProvisionUSN attributes.
+#    B) Other case (it reflect that that provision was done before alpha9)
+#       The base sam.ldb of the reference provision is copied over
+#       the current one, if necessary ldb related to partitions are moved
+#       and renamed
+# The highest used USN is fetched so that changed by upgradeprovision
+# usn can be tracked
+# 12)A Schema object is created, it will be used to provide a complete
+#    schema to current provision during update (as the schema of the
+#    current provision might not be complete and so won't allow some
+#    object to be created)
+# 13)Proceed to full update of sam DB (see the separate paragraph about i)
+# 14)The secrets db is updated by pull all the difference from the reference
+#    provision into the current provision
+# 15)As the previous step has most probably modified the password stored in
+#    in secret for the current DC, a new password is generated,
+#    the kvno is bumped and the entry in samdb is also updated
+# 16)For current provision older than alpha9, we must fix the SD a little bit
+#    administrator to update them because SD used to be generated with the
+#    system account before alpha9.
+# 17)The highest usn modified so far is searched in the database it will be
+#    the upper limit for usn modified during provision.
+#    This is done before potential SD recalculation because we do not want
+#    SD modified during recalculation to be marked as modified during provision
+#    (and so possibly remplaced at next upgradeprovision)
+# 18)Rebuilt SD if the flag indicate to do so
+# 19)Check difference between SD of reference provision and those of the
+#    current provision. The check is done by getting the sddl representation
+#    of the SD. Each sddl in chuncked into parts (user,group,dacl,sacl)
+#    Each part is verified separetly, for dacl and sacl ACL is splited into
+#    ACEs and each ACE is verified separately (so that a permutation in ACE
+#    didn't raise as an error).
+# 20)The oemInfo field is updated to add information about the fact that the
+#    provision has been updated by the upgradeprovision version xxx
+#    (the version is the one obtained when starting samba with the --version
+#    parameter)
+# 21)Check if the current provision has all the settings needed for dynamic
+#    DNS update to work (that is to say the provision is newer than
+#    january 2010). If not dns configuration file from reference provision
+#    are copied in a sub folder and the administrator is invited to
+#    do what is needed.
+# 22)If the lastProvisionUSN attribute was present it is updated to add
+#    the range of usns modified by the current upgradeprovision
+
+
+# About updating the sam DB
+# The update takes place in update_partition function
+# This function read both current and reference provision and list all
+# the available DN of objects
+# If the string representation of a DN in reference provision is
+# equal to the string representation of a DN in current provision
+# (without taking care of case) then the object is flaged as being
+# present. If the object is not present in current provision the object
+# is being flaged as missing in current provision. Object present in current
+# provision but not in reference provision are ignored.
+# Once the list of objects present and missing is done, the deleted object
+# containers are created in the differents partitions (if missing)
+#
+# Then the function add_missing_entries is called
+# This function will go through the list of missing entries by calling
+# add_missing_object for the given object. If this function returns 0
+# it means that the object needs some other object in order to be created
+# The object is reappended at the end of the list to be created later
+# (and preferably after all the needed object have been created)
+# The function keeps on looping on the list of object to be created until
+# it's empty or that the number of defered creation is equal to the number
+# of object that still needs to be created.
+
+# The function add_missing_object will first check if the object can be created.
+# That is to say that it didn't depends other not yet created objects
+# If requisit can't be fullfilled it exists with 0
+# Then it will try to create the missing entry by creating doing
+# an ldb_message_diff between the object in the reference provision and
+# an empty object.
+# This resulting object is filtered to remove all the back link attribute
+# (ie. memberOf) as they will be created by the other linked object (ie.
+# the one with the member attribute)
+# All attributes specified in the hashAttrNotCopied associative array are
+# also removed it's most of the time generated attributes
+
+# After missing entries have been added the update_partition function will
+# take care of object that exist but that need some update.
+# In order to do so the function update_present is called with the list
+# of object that are present in both provision and that might need an update.
+
+# This function handle first case mismatch so that the DN in the current
+# provision have the same case as in reference provision
+
+# It will then construct an associative array consiting of attributes as
+# key and invocationid as value( if the originating invocation id is
+# different from the invocation id of the current DC the value is -1 instead).
+
+# If the range of provision modified attributes is present, the function will
+# use the replMetadataProperty update method which is the following:
+#  Removing attributes that should not be updated: rIDAvailablePool, objectSid,
+#   creationTime, msDs-KeyVersionNumber, oEMInformation
+#  Check for each attribute if its usn is within one of the modified by
+#   provision range and if its originating id is the invocation id of the
+#   current DC, then validate the update from reference to current.
+#   If not or if there is no replMetatdataProperty for this attribute then we
+#   do not update it.
+# Otherwise (case the range of provision modified attribute is not present) it
+# use the following process:
+#  All attributes that need to be added are accepted at the exeption of those
+#   listed in hashOverwrittenAtt, in this case the attribute needs to have the
+#   correct flags specified.
+#  For attributes that need to be modified or removed, a check is performed
+#  in OverwrittenAtt, if the attribute is present and the modification flag
+#  (remove, delete) is one of those listed for this attribute then modification
+#  is accepted. For complicated handling of attribute update, the control is passed
+#  to handle_special_case
+
+
+
+if __name__ == '__main__':
+    global defSDmodified
+    defSDmodified = False
+    # From here start the big steps of the program
+    # 1) First get files paths
+    paths = get_paths(param, smbconf=smbconf)
+    paths.setup = setup_dir
+    # Get ldbs with the system session, it is needed for searching
+    # provision parameters
+    session = system_session()
+
+    # This variable will hold the last provision USN once if it exists.
+    minUSN = 0
+    # 2)
+    ldbs = get_ldbs(paths, creds, session, lp)
+    backupdir = tempfile.mkdtemp(dir=paths.private_dir,
+                                    prefix="backupprovision")
+    backup_provision(paths, backupdir)
+    try:
+        ldbs.startTransactions()
+
+        # 3) Guess all the needed names (variables in fact) from the current
+        # provision.
+        names = find_provision_key_parameters(ldbs.sam, ldbs.secrets, ldbs.idmap,
+                                                paths, smbconf, lp)
+        # 4)
+        lastProvisionUSNs = get_last_provision_usn(ldbs.sam)
+        if lastProvisionUSNs is not None:
+            message(CHANGE,
+                "Find a last provision USN, %d range(s)" % len(lastProvisionUSNs))
+
+        # Objects will be created with the admin session
+        # (not anymore system session)
+        adm_session = admin_session(lp, str(names.domainsid))
+        # So we reget handle on objects
+        # ldbs = get_ldbs(paths, creds, adm_session, lp)
+
+        if not sanitychecks(ldbs.sam, names):
+            message(SIMPLE, "Sanity checks for the upgrade fails, checks messages"
+                            " and correct them before rerunning upgradeprovision")
+            sys.exit(1)
+
+        # Let's see provision parameters
+        print_provision_key_parameters(names)
+
+        # 5) With all this information let's create a fresh new provision used as
+        # reference
+        message(SIMPLE, "Creating a reference provision")
+        provisiondir = tempfile.mkdtemp(dir=paths.private_dir,
+                                        prefix="referenceprovision")
+        newprovision(names, setup_dir, creds, session, smbconf, provisiondir,
+                        provision_logger)
+
+        # TODO
+        # 6) and 7)
+        # We need to get a list of object which SD is directly computed from
+        # defaultSecurityDescriptor.
+        # This will allow us to know which object we can rebuild the SD in case
+        # of change of the parent's SD or of the defaultSD.
+        # Get file paths of this new provision
+        newpaths = get_paths(param, targetdir=provisiondir)
+        new_ldbs = get_ldbs(newpaths, creds, session, lp)
+        new_ldbs.startTransactions()
+
+        # 8) Populate some associative array to ease the update process
+        # List of attribute which are link and backlink
+        populate_links(new_ldbs.sam, names.schemadn)
+        # List of attribute with ASN DN synthax)
+        populate_dnsyntax(new_ldbs.sam, names.schemadn)
+        # 9)
+        update_privilege(newpaths.private_dir, paths.private_dir)
+        # 10)
+        oem = getOEMInfo(ldbs.sam, str(names.rootdn))
+        # Do some modification on sam.ldb
+        ldbs.groupedCommit()
+        new_ldbs.groupedCommit()
+
+        # 11)
+        if re.match(".*alpha((9)|(\d\d+)).*", str(oem)):
+            # 11) A
+            # Starting from alpha9 we can consider that the structure is quite ok
+            # and that we should do only dela
+            delta_update_basesamdb(newpaths.samdb, paths.samdb, creds, session, lp, message)
+        else:
+            # 11) B
+            simple_update_basesamdb(newpaths, paths, names)
+            ldbs = get_ldbs(paths, creds, session, lp)
+            removeProvisionUSN(ldbs.sam)
+
+        ldbs.startTransactions()
+        minUSN = int(str(get_max_usn(ldbs.sam, str(names.rootdn)))) + 1
+        new_ldbs.startTransactions()
+
+        # 12)
+        schema = Schema(setup_path, names.domainsid, schemadn=str(names.schemadn),
+                         serverdn=str(names.serverdn))
+
+        # 13)
+        if opts.full:
+            if not update_samdb(new_ldbs.sam, ldbs.sam, names, lastProvisionUSNs,
+                                schema):
+                message(SIMPLE, "Rollbacking every changes. Check the reason"
+                                " of the problem")
+                message(SIMPLE, "In any case your system as it was before"
+                                " the upgrade")
+                ldbs.groupedRollback()
+                new_ldbs.groupedRollback()
+                shutil.rmtree(provisiondir)
+                sys.exit(1)
+            else:
+                sync_calculated_attributes(ldbs.sam, names)
+        # 14)
+        update_secrets(new_ldbs.secrets, ldbs.secrets, message)
+        # 15)
+        message(SIMPLE, "Update machine account")
+        update_machine_account_password(ldbs.sam, ldbs.secrets, names)
+
+        # 16) SD should be created with admin but as some previous acl were so wrong
+        # that admin can't modify them we have first to recreate them with the good
+        # form but with system account and then give the ownership to admin ...
+        if not re.match(r'.*alpha(9|\d\d+)', str(oem)):
+            message(SIMPLE, "Fixing old povision SD")
+            fix_partition_sd(ldbs.sam, names)
+            rebuild_sd(ldbs.sam, names)
+
+        # We calculate the max USN before recalculating the SD because we might
+        # touch object that have been modified after a provision and we do not
+        # want that the next upgradeprovision thinks that it has a green light
+        # to modify them
+
+        # 17)
+        maxUSN = get_max_usn(ldbs.sam, str(names.rootdn))
+
+        # 18) We rebuild SD only if defaultSecurityDescriptor is modified
+        # But in fact we should do it also if one object has its SD modified as
+        # child might need rebuild
+        if defSDmodified:
+            message(SIMPLE, "Updating SD")
+            ldbs.sam.set_session_info(adm_session)
+            # Alpha10 was a bit broken still
+            if re.match(r'.*alpha(\d|10)', str(oem)):
+                fix_partition_sd(ldbs.sam, names)
+            rebuild_sd(ldbs.sam, names)
+
+        # 19)
+        # Now we are quite confident in the recalculate process of the SD, we make
+        # it optional.
+        # Also the check must be done in a clever way as for the moment we just
+        # compare SDDL
+        if opts.debugchangesd:
+            check_updated_sd(new_ldbs.sam, ldbs.sam, names)
+
+        # 20)
+        updateOEMInfo(ldbs.sam, str(names.rootdn))
+        # 21)
+        check_for_DNS(newpaths.private_dir, paths.private_dir)
+        # 22)
+        if lastProvisionUSNs is not None:
+            update_provision_usn(ldbs.sam, minUSN, maxUSN)
+        if opts.full and (names.policyid is None or names.policyid_dc is None):
+            update_policyids(names, ldbs.sam)
+        if opts.full or opts.resetfileacl:
+            try:
+                update_gpo(paths, ldbs.sam, names, lp, message, 1)
+            except ProvisioningError, e:
+                message(ERROR, "The policy for domain controller is missing,"
+                               " you should restart upgradeprovision with --full")
+        else:
+            try:
+                update_gpo(paths, ldbs.sam, names, lp, message, 0)
+            except ProvisioningError, e:
+                message(ERROR, "The policy for domain controller is missing,"
+                               " you should restart upgradeprovision with --full")
+        ldbs.groupedCommit()
+        new_ldbs.groupedCommit()
+        message(SIMPLE, "Upgrade finished !")
+        # remove reference provision now that everything is done !
+        shutil.rmtree(provisiondir)
+    except StandardError, err:
+        message(ERROR,"A problem has occured when trying to upgrade your provision,"
+                      " a full backup is located at %s" % backupdir)
+        if opts.changeall:
+            (typ, val, tb) = sys.exc_info()
+            traceback.print_exception(typ, val, tb)