Flat file cleanup phase 2: make it work for pg_group. The flat group
authorTom Lane
Sun, 20 Feb 2005 04:45:59 +0000 (04:45 +0000)
committerTom Lane
Sun, 20 Feb 2005 04:45:59 +0000 (04:45 +0000)
file now identifies group members by usesysid not name; this avoids
needing to depend on SearchSysCache which we can't use during startup.
(The old representation was entirely broken anyway, since we did not
regenerate the file following RENAME USER.)  It's only a 95% solution
because if the group membership list is big enough to be toasted out
of line, we cannot read it during startup.  I think this will do for
the moment, until we have time to implement the planned pg_role
replacement for pg_group.

src/backend/libpq/crypt.c
src/backend/libpq/hba.c
src/backend/utils/init/flatfiles.c

index fcc7db2112d4d4c3cf98bb1b9d30f97f9660eff8..58e80334f6100afa7bec00a891f799ea8c4f460a 100644 (file)
@@ -9,7 +9,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/backend/libpq/crypt.c,v 1.61 2004/12/31 21:59:50 pgsql Exp $
+ * $PostgreSQL: pgsql/src/backend/libpq/crypt.c,v 1.62 2005/02/20 04:45:57 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -42,14 +42,18 @@ md5_crypt_verify(const Port *port, const char *user, char *client_pass)
    if ((line = get_user_line(user)) == NULL)
        return STATUS_ERROR;
 
-   /* Skip over username */
-   token = lnext(list_head(*line));
+   /* Skip over username and usesysid */
+   token = list_head(*line);
+   if (token)
+       token = lnext(token);
+   if (token)
+       token = lnext(token);
    if (token)
    {
-       shadow_pass = lfirst(token);
+       shadow_pass = (char *) lfirst(token);
        token = lnext(token);
        if (token)
-           valuntil = lfirst(token);
+           valuntil = (char *) lfirst(token);
    }
 
    if (shadow_pass == NULL || *shadow_pass == '\0')
@@ -142,16 +146,14 @@ md5_crypt_verify(const Port *port, const char *user, char *client_pass)
        /*
         * Password OK, now check to be sure we are not past valuntil
         */
-       AbsoluteTime vuntil,
-                   current;
+       AbsoluteTime vuntil;
 
-       if (!valuntil)
+       if (valuntil == NULL || *valuntil == '\0')
            vuntil = INVALID_ABSTIME;
        else
            vuntil = DatumGetAbsoluteTime(DirectFunctionCall1(abstimein,
                                             CStringGetDatum(valuntil)));
-       current = GetCurrentAbsoluteTime();
-       if (vuntil != INVALID_ABSTIME && vuntil < current)
+       if (vuntil != INVALID_ABSTIME && vuntil < GetCurrentAbsoluteTime())
            retval = STATUS_ERROR;
        else
            retval = STATUS_OK;
index f96eaec0f2d7e0ff73227d0097a31fba7e52f548..9a65ee1610c09fe99aefd04fe39289abaa6d1207 100644 (file)
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/libpq/hba.c,v 1.138 2005/02/20 02:21:40 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/libpq/hba.c,v 1.139 2005/02/20 04:45:57 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -498,23 +498,28 @@ get_user_line(const char *user)
 
 
 /*
- * Check group for a specific user.
+ * Does user belong to group?
  */
 static bool
 check_group(char *group, char *user)
 {
    List      **line;
+   ListCell   *line_item;
+   char       *usesysid;
 
-   if ((line = get_group_line(group)) != NULL)
-   {
-       ListCell   *line_item;
+   if ((line = get_user_line(user)) == NULL)
+       return false;           /* if user not exist, say "no" */
+   /* Skip over username to get usesysid */
+   usesysid = (char *) lsecond(*line);
 
-       /* skip over the group name */
-       for_each_cell(line_item, lnext(list_head(*line)))
-       {
-           if (strcmp(lfirst(line_item), user) == 0)
-               return true;
-       }
+   if ((line = get_group_line(group)) == NULL)
+       return false;           /* if group not exist, say "no" */
+
+   /* skip over the group name, examine all the member usesysid's */
+   for_each_cell(line_item, lnext(list_head(*line)))
+   {
+       if (strcmp((char *) lfirst(line_item), usesysid) == 0)
+           return true;
    }
 
    return false;
index c54badea8e20fed4dc508f14cf3e7984b1aa65f7..0cbb878f9ffc04f9e9021d4cc16bfc9fe3f3b86e 100644 (file)
@@ -22,7 +22,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/backend/utils/init/flatfiles.c,v 1.1 2005/02/20 02:22:00 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/init/flatfiles.c,v 1.2 2005/02/20 04:45:59 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -278,7 +278,7 @@ write_database_file(Relation drel)
        }
 
        /*
-        * File format is: "dbname" oid frozenxid
+        * The file format is: "dbname" oid frozenxid
         *
         * The xid is not needed for backend startup, but may be of use
         * for forensic purposes.
@@ -317,13 +317,6 @@ write_database_file(Relation drel)
 
 /*
  * write_group_file: update the flat group file
- *
- * XXX this will never be able to work during system bootstrap: we don't
- * have either TOAST support or SysCache support.  Need to redefine both
- * the catalog and file contents to fix this completely.  In the short term
- * we can handle everything except an out-of-line-toasted grolist, if we
- * change the flat file definition to store numeric sysids instead of
- * user names.
  */
 static void
 write_group_file(Relation grel)
@@ -335,7 +328,6 @@ write_group_file(Relation grel)
    mode_t      oumask;
    HeapScanDesc scan;
    HeapTuple   tuple;
-   TupleDesc   dsc = RelationGetDescr(grel);
 
    /*
     * Create a temporary filename to be renamed later.  This prevents the
@@ -364,22 +356,19 @@ write_group_file(Relation grel)
    scan = heap_beginscan(grel, SnapshotSelf, 0, NULL);
    while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
    {
-       Datum       datum,
-                   grolist_datum;
-       bool        isnull;
+       Form_pg_group grpform = (Form_pg_group) GETSTRUCT(tuple);
+       HeapTupleHeader tup = tuple->t_data;
+       char       *tp;             /* ptr to tuple data */
+       long        off;            /* offset in tuple data */
+       bits8      *bp = tup->t_bits;   /* ptr to null bitmask in tuple */
+       Datum       datum;
        char       *groname;
        IdList     *grolist_p;
        AclId      *aidp;
        int         i,
                    num;
-       char       *usename;
-       bool        first_user = true;
 
-       datum = heap_getattr(tuple, Anum_pg_group_groname, dsc, &isnull);
-       /* ignore NULL groupnames --- shouldn't happen */
-       if (isnull)
-           continue;
-       groname = NameStr(*DatumGetName(datum));
+       groname = NameStr(grpform->groname);
 
        /*
         * Check for illegal characters in the group name.
@@ -391,57 +380,58 @@ write_group_file(Relation grel)
            continue;
        }
 
-       grolist_datum = heap_getattr(tuple, Anum_pg_group_grolist, dsc, &isnull);
-       /* Ignore NULL group lists */
-       if (isnull)
+       /*
+        * We can't use heap_getattr() here because during startup we will
+        * not have any tupdesc for pg_group.  Fortunately it's not too
+        * hard to work around this.  grolist is the first possibly-null
+        * field so we can compute its offset directly.
+        */
+       tp = (char *) tup + tup->t_hoff;
+       off = offsetof(FormData_pg_group, grolist);
+
+       if (HeapTupleHasNulls(tuple) &&
+           att_isnull(Anum_pg_group_grolist - 1, bp))
+       {
+           /* grolist is null, so we can ignore this group */
+           continue;
+       }
+
+       /* assume grolist is pass-by-ref */
+       datum = PointerGetDatum(tp + off);
+
+       /*
+        * We can't currently support out-of-line toasted group lists in
+        * startup mode (the tuptoaster won't work).  This sucks, but it
+        * should be something of a corner case.  Live with it until we
+        * can redesign pg_group.
+        *
+        * Detect startup mode by noting whether we got a tupdesc.
+        */
+       if (VARATT_IS_EXTERNAL(DatumGetPointer(datum)) &&
+           RelationGetDescr(grel) == NULL)
            continue;
 
        /* be sure the IdList is not toasted */
-       grolist_p = DatumGetIdListP(grolist_datum);
+       grolist_p = DatumGetIdListP(datum);
 
-       /* scan grolist */
-       num = IDLIST_NUM(grolist_p);
+       /*
+        * The file format is: "groupname"    usesysid1 usesysid2 ...
+        *
+        * We ignore groups that have no members.
+        */
        aidp = IDLIST_DAT(grolist_p);
-       for (i = 0; i < num; ++i)
+       num = IDLIST_NUM(grolist_p);
+       if (num > 0)
        {
-           tuple = SearchSysCache(SHADOWSYSID,
-                                  PointerGetDatum(aidp[i]),
-                                  0, 0, 0);
-           if (HeapTupleIsValid(tuple))
-           {
-               usename = NameStr(((Form_pg_shadow) GETSTRUCT(tuple))->usename);
-
-               /*
-                * Check for illegal characters in the user name.
-                */
-               if (!name_okay(usename))
-               {
-                   ereport(LOG,
-                         (errmsg("invalid user name \"%s\"", usename)));
-                   continue;
-               }
-
-               /*
-                * File format is: "groupname"    "user1" "user2" "user3"
-                */
-               if (first_user)
-               {
-                   fputs_quote(groname, fp);
-                   fputs("\t", fp);
-                   first_user = false;
-               }
-               else
-                   fputs(" ", fp);
-
-               fputs_quote(usename, fp);
-
-               ReleaseSysCache(tuple);
-           }
-       }
-       if (!first_user)
+           fputs_quote(groname, fp);
+           fprintf(fp, "\t%u", aidp[0]);
+           for (i = 1; i < num; ++i)
+               fprintf(fp, " %u", aidp[i]);
            fputs("\n", fp);
+       }
+
        /* if IdList was toasted, free detoasted copy */
-       if ((Pointer) grolist_p != DatumGetPointer(grolist_datum))
+       if ((Pointer) grolist_p != DatumGetPointer(datum))
            pfree(grolist_p);
    }
    heap_endscan(scan);
@@ -517,8 +507,10 @@ write_user_file(Relation urel)
        char       *usename,
                   *passwd,
                   *valuntil;
+       AclId       usesysid;
 
        usename = NameStr(pwform->usename);
+       usesysid = pwform->usesysid;
 
        /*
         * We can't use heap_getattr() here because during startup we will
@@ -532,30 +524,26 @@ write_user_file(Relation urel)
        if (HeapTupleHasNulls(tuple) &&
            att_isnull(Anum_pg_shadow_passwd - 1, bp))
        {
-           /*
-            * It can be argued that people having a null password shouldn't
-            * be allowed to connect under password authentication, because
-            * they need to have a password set up first. If you think
-            * assuming an empty password in that case is better, change this
-            * logic to look something like the code for valuntil.
-            */
-           continue;
+           /* passwd is null, emit as an empty string */
+           passwd = pstrdup("");
        }
+       else
+       {
+           /* assume passwd is pass-by-ref */
+           datum = PointerGetDatum(tp + off);
 
-       /* assume passwd is pass-by-ref */
-       datum = PointerGetDatum(tp + off);
-
-       /*
-        * The password probably shouldn't ever be out-of-line toasted;
-        * if it is, ignore it, since we can't handle that in startup mode.
-        */
-       if (VARATT_IS_EXTERNAL(DatumGetPointer(datum)))
-           continue;
-
-       passwd = DatumGetCString(DirectFunctionCall1(textout, datum));
+           /*
+            * The password probably shouldn't ever be out-of-line toasted;
+            * if it is, ignore it, since we can't handle that in startup mode.
+            */
+           if (VARATT_IS_EXTERNAL(DatumGetPointer(datum)))
+               passwd = pstrdup("");
+           else
+               passwd = DatumGetCString(DirectFunctionCall1(textout, datum));
 
-       /* assume passwd has attlen -1 */
-       off = att_addlength(off, -1, tp + off);
+           /* assume passwd has attlen -1 */
+           off = att_addlength(off, -1, tp + off);
+       }
 
        if (HeapTupleHasNulls(tuple) &&
            att_isnull(Anum_pg_shadow_valuntil - 1, bp))
@@ -588,8 +576,11 @@ write_user_file(Relation urel)
            continue;
        }
 
+       /*
+        * The file format is: "usename" usesysid "passwd" "valuntil"
+        */
        fputs_quote(usename, fp);
-       fputs(" ", fp);
+       fprintf(fp, " %u ", usesysid);
        fputs_quote(passwd, fp);
        fputs(" ", fp);
        fputs_quote(valuntil, fp);
@@ -664,9 +655,6 @@ BuildFlatFiles(bool database_only)
 
    if (!database_only)
    {
-#ifdef NOT_YET
-       /* XXX doesn't work yet for reasons stated above */
-
        /* hard-wired path to pg_group */
        rnode.spcNode = GLOBALTABLESPACE_OID;
        rnode.dbNode = 0;
@@ -674,7 +662,6 @@ BuildFlatFiles(bool database_only)
 
        rel = XLogOpenRelation(true, 0, rnode);
        write_group_file(rel);
-#endif
 
        /* hard-wired path to pg_shadow */
        rnode.spcNode = GLOBALTABLESPACE_OID;