Cause ALTER OWNER commands to update the object's ACL, replacing references
authorTom Lane
Sun, 1 Aug 2004 20:30:49 +0000 (20:30 +0000)
committerTom Lane
Sun, 1 Aug 2004 20:30:49 +0000 (20:30 +0000)
to the old owner with the new owner.  This is not necessarily right, but
it's sure a lot more likely to be what the user wants than doing nothing.
Christopher Kings-Lynne, some rework by Tom Lane.

src/backend/commands/dbcommands.c
src/backend/commands/functioncmds.c
src/backend/commands/schemacmds.c
src/backend/commands/tablecmds.c
src/backend/commands/tablespace.c
src/backend/utils/adt/acl.c
src/include/utils/acl.h

index bacf5aa31af92ddf979313b30556523575e7c05b..1c1907c5e453d5c3bf2903430fb30d31c9ba6814 100644 (file)
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/commands/dbcommands.c,v 1.138 2004/08/01 06:19:22 momjian Exp $
+ *   $PostgreSQL: pgsql/src/backend/commands/dbcommands.c,v 1.139 2004/08/01 20:30:48 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -768,8 +768,7 @@ AlterDatabaseSet(AlterDatabaseSetStmt *stmt)
 void
 AlterDatabaseOwner(const char *dbname, AclId newOwnerSysId)
 {
-   HeapTuple   tuple,
-               newtuple;
+   HeapTuple   tuple;
    Relation    rel;
    ScanKeyData scankey;
    SysScanDesc scan;
@@ -788,8 +787,7 @@ AlterDatabaseOwner(const char *dbname, AclId newOwnerSysId)
                (errcode(ERRCODE_UNDEFINED_DATABASE),
                 errmsg("database \"%s\" does not exist", dbname)));
 
-   newtuple = heap_copytuple(tuple);
-   datForm = (Form_pg_database) GETSTRUCT(newtuple);
+   datForm = (Form_pg_database) GETSTRUCT(tuple);
 
    /* 
     * If the new owner is the same as the existing owner, consider the
@@ -797,6 +795,14 @@ AlterDatabaseOwner(const char *dbname, AclId newOwnerSysId)
     */
    if (datForm->datdba != newOwnerSysId)
    {
+       Datum       repl_val[Natts_pg_database];
+       char        repl_null[Natts_pg_database];
+       char        repl_repl[Natts_pg_database];
+       Acl     *newAcl;
+       Datum       aclDatum;
+       bool        isNull;
+       HeapTuple   newtuple;
+
        /* changing owner's database for someone else: must be superuser */
        /* note that the someone else need not have any permissions */
        if (!superuser())
@@ -804,10 +810,33 @@ AlterDatabaseOwner(const char *dbname, AclId newOwnerSysId)
                    (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
                     errmsg("must be superuser to change owner")));
 
-       /* change owner */
-       datForm->datdba = newOwnerSysId;
+       memset(repl_null, ' ', sizeof(repl_null));
+       memset(repl_repl, ' ', sizeof(repl_repl));
+
+       repl_repl[Anum_pg_database_datdba - 1] = 'r';
+       repl_val[Anum_pg_database_datdba - 1] = Int32GetDatum(newOwnerSysId);
+
+       /*
+        * Determine the modified ACL for the new owner.  This is only
+        * necessary when the ACL is non-null.
+        */
+       aclDatum = heap_getattr(tuple,
+                           Anum_pg_database_datacl,
+                           RelationGetDescr(rel),
+                           &isNull);
+       if (!isNull)
+       {
+           newAcl = aclnewowner(DatumGetAclP(aclDatum),
+                                datForm->datdba, newOwnerSysId);
+           repl_repl[Anum_pg_database_datacl - 1] = 'r';
+           repl_val[Anum_pg_database_datacl - 1] = PointerGetDatum(newAcl);
+       }
+
+       newtuple = heap_modifytuple(tuple, rel, repl_val, repl_null, repl_repl);
        simple_heap_update(rel, &newtuple->t_self, newtuple);
        CatalogUpdateIndexes(rel, newtuple);
+
+       heap_freetuple(newtuple);
    }
 
    systable_endscan(scan);
index 20ee9fa3445bd01f86fd8e78a9ab445837431df6..58d767d4f76ea2b7a756813edb113d6ba7b1df71 100644 (file)
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/commands/functioncmds.c,v 1.49 2004/06/25 21:55:53 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/commands/functioncmds.c,v 1.50 2004/08/01 20:30:48 tgl Exp $
  *
  * DESCRIPTION
  *   These routines take the parse tree and pick out the
@@ -738,7 +738,7 @@ AlterFunctionOwner(List *name, List *argtypes, AclId newOwnerSysId)
 
    procOid = LookupFuncNameTypeNames(name, argtypes, false);
 
-   tup = SearchSysCacheCopy(PROCOID,
+   tup = SearchSysCache(PROCOID,
                             ObjectIdGetDatum(procOid),
                             0, 0, 0);
    if (!HeapTupleIsValid(tup)) /* should not happen */
@@ -758,22 +758,51 @@ AlterFunctionOwner(List *name, List *argtypes, AclId newOwnerSysId)
     */
    if (procForm->proowner != newOwnerSysId)
    {
+       Datum       repl_val[Natts_pg_proc];
+       char        repl_null[Natts_pg_proc];
+       char        repl_repl[Natts_pg_proc];
+       Acl     *newAcl;
+       Datum       aclDatum;
+       bool        isNull;
+       HeapTuple   newtuple;
+
        /* Otherwise, must be superuser to change object ownership */
        if (!superuser())
            ereport(ERROR,
                    (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
                     errmsg("must be superuser to change owner")));
 
-       /* Modify the owner --- okay to scribble on tup because it's a copy */
-       procForm->proowner = newOwnerSysId;
+       memset(repl_null, ' ', sizeof(repl_null));
+       memset(repl_repl, ' ', sizeof(repl_repl));
+
+       repl_repl[Anum_pg_proc_proowner - 1] = 'r';
+       repl_val[Anum_pg_proc_proowner - 1] = Int32GetDatum(newOwnerSysId);
+
+       /*
+        * Determine the modified ACL for the new owner.  This is only
+        * necessary when the ACL is non-null.
+        */
+       aclDatum = SysCacheGetAttr(PROCOID, tup,
+                                  Anum_pg_proc_proacl,
+                                  &isNull);
+       if (!isNull)
+       {
+           newAcl = aclnewowner(DatumGetAclP(aclDatum),
+                                procForm->proowner, newOwnerSysId);
+           repl_repl[Anum_pg_proc_proacl - 1] = 'r';
+           repl_val[Anum_pg_proc_proacl - 1] = PointerGetDatum(newAcl);
+       }
+
+       newtuple = heap_modifytuple(tup, rel, repl_val, repl_null, repl_repl);
 
-       simple_heap_update(rel, &tup->t_self, tup);
+       simple_heap_update(rel, &newtuple->t_self, newtuple);
+       CatalogUpdateIndexes(rel, newtuple);
 
-       CatalogUpdateIndexes(rel, tup);
+       heap_freetuple(newtuple);
    }
 
+   ReleaseSysCache(tup);
    heap_close(rel, NoLock);
-   heap_freetuple(tup);
 }
 
 
index 35e18c9bfbd3df7739ca81c3fa7d7b4a014a9e65..2dead1e15a123b4a7390266789049b49cfa54ed3 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/commands/schemacmds.c,v 1.20 2004/06/25 21:55:53 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/commands/schemacmds.c,v 1.21 2004/08/01 20:30:48 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -320,7 +320,7 @@ AlterSchemaOwner(const char *name, AclId newOwnerSysId)
 
    rel = heap_openr(NamespaceRelationName, RowExclusiveLock);
 
-   tup = SearchSysCacheCopy(NAMESPACENAME,
+   tup = SearchSysCache(NAMESPACENAME,
                             CStringGetDatum(name),
                             0, 0, 0);
    if (!HeapTupleIsValid(tup))
@@ -335,20 +335,49 @@ AlterSchemaOwner(const char *name, AclId newOwnerSysId)
     */
    if (nspForm->nspowner != newOwnerSysId)
    {
+       Datum       repl_val[Natts_pg_namespace];
+       char        repl_null[Natts_pg_namespace];
+       char        repl_repl[Natts_pg_namespace];
+       Acl     *newAcl;
+       Datum       aclDatum;
+       bool        isNull;
+       HeapTuple   newtuple;
+
        /* Otherwise, must be superuser to change object ownership */
        if (!superuser())
            ereport(ERROR,
                    (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
                     errmsg("must be superuser to change owner")));
 
-       /* Modify the owner --- okay to scribble on tup because it's a copy */
-       nspForm->nspowner = newOwnerSysId;
+       memset(repl_null, ' ', sizeof(repl_null));
+       memset(repl_repl, ' ', sizeof(repl_repl));
 
-       simple_heap_update(rel, &tup->t_self, tup);
+       repl_repl[Anum_pg_namespace_nspowner - 1] = 'r';
+       repl_val[Anum_pg_namespace_nspowner - 1] = Int32GetDatum(newOwnerSysId);
 
-       CatalogUpdateIndexes(rel, tup);
-   }
+       /*
+        * Determine the modified ACL for the new owner.  This is only
+        * necessary when the ACL is non-null.
+        */
+       aclDatum = SysCacheGetAttr(NAMESPACENAME, tup,
+                                  Anum_pg_namespace_nspacl,
+                                  &isNull);
+       if (!isNull)
+       {
+           newAcl = aclnewowner(DatumGetAclP(aclDatum),
+                                nspForm->nspowner, newOwnerSysId);
+           repl_repl[Anum_pg_namespace_nspacl - 1] = 'r';
+           repl_val[Anum_pg_namespace_nspacl - 1] = PointerGetDatum(newAcl);
+       }
+
+       newtuple = heap_modifytuple(tup, rel, repl_val, repl_null, repl_repl);
+
+       simple_heap_update(rel, &newtuple->t_self, newtuple);
+       CatalogUpdateIndexes(rel, newtuple);
 
+       heap_freetuple(newtuple);
+   }
+   
+   ReleaseSysCache(tup);
    heap_close(rel, NoLock);
-   heap_freetuple(tup);
 }
index e78db91e77af06d9a3a15cf169da0afe25cd0ba2..b69e386990ee1f021d951c1ee13abb03e13550fe 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.122 2004/07/21 22:31:21 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.123 2004/08/01 20:30:48 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -5115,7 +5115,7 @@ ATExecChangeOwner(Oid relationOid, int32 newOwnerSysId)
    /* Get its pg_class tuple, too */
    class_rel = heap_openr(RelationRelationName, RowExclusiveLock);
 
-   tuple = SearchSysCacheCopy(RELOID,
+   tuple = SearchSysCache(RELOID,
                               ObjectIdGetDatum(relationOid),
                               0, 0, 0);
    if (!HeapTupleIsValid(tuple))
@@ -5145,21 +5145,47 @@ ATExecChangeOwner(Oid relationOid, int32 newOwnerSysId)
     */
    if (tuple_class->relowner != newOwnerSysId)
    {
+       Datum       repl_val[Natts_pg_class];
+       char        repl_null[Natts_pg_class];
+       char        repl_repl[Natts_pg_class];
+       Acl     *newAcl;
+       Datum       aclDatum;
+       bool        isNull;
+       HeapTuple   newtuple;
+
        /* Otherwise, check that we are the superuser */
        if (!superuser())
            ereport(ERROR,
                (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
            errmsg("must be superuser to change owner")));
 
+       memset(repl_null, ' ', sizeof(repl_null));
+       memset(repl_repl, ' ', sizeof(repl_repl));
+
+       repl_repl[Anum_pg_class_relowner - 1] = 'r';
+       repl_val[Anum_pg_class_relowner - 1] = Int32GetDatum(newOwnerSysId);
+
        /*
-        * Okay, this is a valid tuple: change its ownership and write to the
-        * heap.
+        * Determine the modified ACL for the new owner.  This is only
+        * necessary when the ACL is non-null.
         */
-       tuple_class->relowner = newOwnerSysId;
-       simple_heap_update(class_rel, &tuple->t_self, tuple);
+       aclDatum = SysCacheGetAttr(RELOID, tuple,
+                                  Anum_pg_class_relacl,
+                                  &isNull);
+       if (!isNull)
+       {
+           newAcl = aclnewowner(DatumGetAclP(aclDatum),
+                                tuple_class->relowner, newOwnerSysId);
+           repl_repl[Anum_pg_class_relacl - 1] = 'r';
+           repl_val[Anum_pg_class_relacl - 1] = PointerGetDatum(newAcl);
+       }
 
-       /* Keep the catalog indexes up to date */
-       CatalogUpdateIndexes(class_rel, tuple);
+       newtuple = heap_modifytuple(tuple, class_rel, repl_val, repl_null, repl_repl);
+
+       simple_heap_update(class_rel, &newtuple->t_self, newtuple);
+       CatalogUpdateIndexes(class_rel, newtuple);
+
+       heap_freetuple(newtuple);
 
        /*
         * If we are operating on a table, also change the ownership of any
@@ -5190,7 +5216,7 @@ ATExecChangeOwner(Oid relationOid, int32 newOwnerSysId)
        }
    }
 
-   heap_freetuple(tuple);
+   ReleaseSysCache(tuple);
    heap_close(class_rel, RowExclusiveLock);
    relation_close(target_rel, NoLock);
 }
index a08c12305a76f5afb692e49af1c1e598e35c06e2..1a9e69dc12398cd4d7ad7acd4fd9610a23a1dc12 100644 (file)
@@ -45,7 +45,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/commands/tablespace.c,v 1.6 2004/07/11 19:52:49 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/commands/tablespace.c,v 1.7 2004/08/01 20:30:48 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -757,7 +757,6 @@ AlterTableSpaceOwner(const char *name, AclId newOwnerSysId)
    HeapScanDesc scandesc;
    Form_pg_tablespace spcForm;
    HeapTuple   tup;
-   HeapTuple   newtuple;
 
    /* Search pg_tablespace */
    rel = heap_openr(TableSpaceRelationName, RowExclusiveLock);
@@ -773,8 +772,7 @@ AlterTableSpaceOwner(const char *name, AclId newOwnerSysId)
                (errcode(ERRCODE_UNDEFINED_OBJECT),
                 errmsg("tablespace \"%s\" does not exist", name)));
 
-   newtuple = heap_copytuple(tup);
-   spcForm = (Form_pg_tablespace) GETSTRUCT(newtuple);
+   spcForm = (Form_pg_tablespace) GETSTRUCT(tup);
 
    /* 
     * If the new owner is the same as the existing owner, consider the
@@ -782,16 +780,48 @@ AlterTableSpaceOwner(const char *name, AclId newOwnerSysId)
     */
    if (spcForm->spcowner != newOwnerSysId)
    {
+       Datum       repl_val[Natts_pg_tablespace];
+       char        repl_null[Natts_pg_tablespace];
+       char        repl_repl[Natts_pg_tablespace];
+       Acl     *newAcl;
+       Datum       aclDatum;
+       bool        isNull;
+       HeapTuple   newtuple;
+
        /* Otherwise, must be superuser to change object ownership */
        if (!superuser())
            ereport(ERROR,
                    (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
                     errmsg("must be superuser to change owner")));
 
-       /* Modify the owner */
-       spcForm->spcowner = newOwnerSysId;
+       memset(repl_null, ' ', sizeof(repl_null));
+       memset(repl_repl, ' ', sizeof(repl_repl));
+
+       repl_repl[Anum_pg_tablespace_spcowner - 1] = 'r';
+       repl_val[Anum_pg_tablespace_spcowner - 1] = Int32GetDatum(newOwnerSysId);
+
+       /*
+        * Determine the modified ACL for the new owner.  This is only
+        * necessary when the ACL is non-null.
+        */
+       aclDatum = heap_getattr(tup,
+                           Anum_pg_tablespace_spcacl,
+                           RelationGetDescr(rel),
+                           &isNull);
+       if (!isNull)
+       {
+           newAcl = aclnewowner(DatumGetAclP(aclDatum),
+                                spcForm->spcowner, newOwnerSysId);
+           repl_repl[Anum_pg_tablespace_spcacl - 1] = 'r';
+           repl_val[Anum_pg_tablespace_spcacl - 1] = PointerGetDatum(newAcl);
+       }
+
+       newtuple = heap_modifytuple(tup, rel, repl_val, repl_null, repl_repl);
+
        simple_heap_update(rel, &newtuple->t_self, newtuple);
        CatalogUpdateIndexes(rel, newtuple);
+
+       heap_freetuple(newtuple);
    }
 
    heap_endscan(scandesc);
index 4b45ca7c4d479f2345af028de5758b3958e9439a..290c3ed49d2d04335e0531b9706af0af0bda2ca7 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/utils/adt/acl.c,v 1.107 2004/07/12 20:23:50 momjian Exp $
+ *   $PostgreSQL: pgsql/src/backend/utils/adt/acl.c,v 1.108 2004/08/01 20:30:49 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -717,6 +717,109 @@ aclupdate(const Acl *old_acl, const AclItem *mod_aip,
    return new_acl;
 }
 
+/*
+ * Update an ACL array to reflect a change of owner to the parent object
+ *
+ * old_acl: the input ACL array (must not be NULL)
+ * oldownerid: AclId of the old object owner
+ * newownerid: AclId of the new object owner
+ *
+ * The result is a modified copy; the input object is not changed.
+ *
+ * NB: caller is responsible for having detoasted the input ACL, if needed.
+ */
+Acl *
+aclnewowner(const Acl *old_acl, AclId oldownerid, AclId newownerid)
+{
+   Acl        *new_acl;
+   AclItem    *new_aip;
+   AclItem    *old_aip;
+   AclItem    *dst_aip;
+   AclItem    *src_aip;
+   AclItem    *targ_aip;
+   bool        newpresent = false;
+   int         dst,
+               src,
+               targ,
+               num;
+
+   /*
+    * Make a copy of the given ACL, substituting new owner ID for old
+    * wherever it appears as either grantor or grantee.  Also note if
+    * the new owner ID is already present.
+    */
+   num = ACL_NUM(old_acl);
+   old_aip = ACL_DAT(old_acl);
+   new_acl = allocacl(num);
+   new_aip = ACL_DAT(new_acl);
+   memcpy(new_aip, old_aip, num * sizeof(AclItem));
+   for (dst = 0, dst_aip = new_aip; dst < num; dst++, dst_aip++)
+   {
+       /* grantor is always a UID, but grantee might not be */
+       if (dst_aip->ai_grantor == oldownerid)
+           dst_aip->ai_grantor = newownerid;
+       else if (dst_aip->ai_grantor == newownerid)
+           newpresent = true;
+       if (ACLITEM_GET_IDTYPE(*dst_aip) == ACL_IDTYPE_UID)
+       {
+           if (dst_aip->ai_grantee == oldownerid)
+               dst_aip->ai_grantee = newownerid;
+           else if (dst_aip->ai_grantee == newownerid)
+               newpresent = true;
+       }
+   }
+
+   /*
+    * If the old ACL contained any references to the new owner, then we
+    * may now have generated an ACL containing duplicate entries.  Find
+    * them and merge them so that there are not duplicates.  (This is
+    * relatively expensive since we use a stupid O(N^2) algorithm, but
+    * it's unlikely to be the normal case.)
+    *
+    * To simplify deletion of duplicate entries, we temporarily leave them
+    * in the array but set their privilege masks to zero; when we reach
+    * such an entry it's just skipped.  (Thus, a side effect of this code
+    * will be to remove privilege-free entries, should there be any in the
+    * input.)  dst is the next output slot, targ is the currently considered
+    * input slot (always >= dst), and src scans entries to the right of targ
+    * looking for duplicates.  Once an entry has been emitted to dst it is
+    * known duplicate-free and need not be considered anymore.
+    */
+   if (newpresent)
+   {
+       dst = 0;
+       for (targ = 0, targ_aip = new_aip; targ < num; targ++, targ_aip++)
+       {
+           /* ignore if deleted in an earlier pass */
+           if (ACLITEM_GET_RIGHTS(*targ_aip) == ACL_NO_RIGHTS)
+               continue;
+           /* find and merge any duplicates */
+           for (src = targ + 1, src_aip = targ_aip + 1; src < num;
+                src++, src_aip++)
+           {
+               if (ACLITEM_GET_RIGHTS(*src_aip) == ACL_NO_RIGHTS)
+                   continue;
+               if (aclitem_match(targ_aip, src_aip))
+               {
+                   ACLITEM_SET_RIGHTS(*targ_aip,
+                                      ACLITEM_GET_RIGHTS(*targ_aip) |
+                                      ACLITEM_GET_RIGHTS(*src_aip));
+                   /* mark the duplicate deleted */
+                   ACLITEM_SET_RIGHTS(*src_aip, ACL_NO_RIGHTS);
+               }
+           }
+           /* and emit to output */
+           new_aip[dst] = *targ_aip;
+           dst++;
+       }
+       /* Adjust array size to be 'dst' items */
+       ARR_DIMS(new_acl)[0] = dst;
+       ARR_SIZE(new_acl) = ACL_N_SIZE(dst);
+   }
+
+   return new_acl;
+}
+
 
 /*
  * When granting grant options, we must disallow attempts to set up circular
@@ -2373,15 +2476,15 @@ convert_tablespace_name(text *tablespacename)
 {
    char            *spcname;
    Oid         oid;
-   
+
    spcname = DatumGetCString(DirectFunctionCall1(textout,
-                                                           PointerGetDatum(tablespacename)));
+                                                 PointerGetDatum(tablespacename)));
    oid = get_tablespace_oid(spcname);
 
-        if (!OidIsValid(oid))
-                ereport(ERROR,
-                                (errcode(ERRCODE_UNDEFINED_OBJECT),
-                                 errmsg("tablespace \"%s\" does not exist", spcname)));
+   if (!OidIsValid(oid))
+       ereport(ERROR,
+               (errcode(ERRCODE_UNDEFINED_OBJECT),
+                errmsg("tablespace \"%s\" does not exist", spcname)));
 
    return oid;
 }
index f7779be589d37b0cd78e9c0a3c0abdf77d98fbf7..cf13038ad004be74ee9f117ba2a6f5b768580b4e 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/utils/acl.h,v 1.71 2004/06/18 06:14:21 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/utils/acl.h,v 1.72 2004/08/01 20:30:49 tgl Exp $
  *
  * NOTES
  *   An ACL array is simply an array of AclItems, representing the union
@@ -224,6 +224,8 @@ typedef enum AclObjectKind
 extern Acl *acldefault(GrantObjectType objtype, AclId ownerid);
 extern Acl *aclupdate(const Acl *old_acl, const AclItem *mod_aip,
                      int modechg, AclId ownerid, DropBehavior behavior);
+extern Acl *aclnewowner(const Acl *old_acl, AclId oldownerid, AclId newownerid);
+                     
 extern AclMode aclmask(const Acl *acl, AclId userid, AclId ownerid,
                       AclMode mask, AclMaskHow how);