Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * Utility routines for SQL dumping
4 : : *
5 : : * Basically this is stuff that is useful in both pg_dump and pg_dumpall.
6 : : *
7 : : *
8 : : * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
9 : : * Portions Copyright (c) 1994, Regents of the University of California
10 : : *
11 : : * src/bin/pg_dump/dumputils.c
12 : : *
13 : : *-------------------------------------------------------------------------
14 : : */
15 : : #include "postgres_fe.h"
16 : :
17 : : #include <ctype.h>
18 : :
19 : : #include "common/file_perm.h"
20 : : #include "common/logging.h"
21 : : #include "dumputils.h"
22 : : #include "fe_utils/string_utils.h"
23 : :
24 : : static const char restrict_chars[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
25 : :
26 : : static bool parseAclItem(const char *item, const char *type,
27 : : const char *name, const char *subname, int remoteVersion,
28 : : PQExpBuffer grantee, PQExpBuffer grantor,
29 : : PQExpBuffer privs, PQExpBuffer privswgo);
30 : : static char *dequoteAclUserName(PQExpBuffer output, char *input);
31 : : static void AddAcl(PQExpBuffer aclbuf, const char *keyword,
32 : : const char *subname);
33 : :
34 : :
35 : : /*
36 : : * Sanitize a string to be included in an SQL comment or TOC listing, by
37 : : * replacing any newlines with spaces. This ensures each logical output line
38 : : * is in fact one physical output line, to prevent corruption of the dump
39 : : * (which could, in the worst case, present an SQL injection vulnerability
40 : : * if someone were to incautiously load a dump containing objects with
41 : : * maliciously crafted names).
42 : : *
43 : : * The result is a freshly malloc'd string. If the input string is NULL,
44 : : * return a malloc'ed empty string, unless want_hyphen, in which case return a
45 : : * malloc'ed hyphen.
46 : : *
47 : : * Note that we currently don't bother to quote names, meaning that the name
48 : : * fields aren't automatically parseable. "pg_restore -L" doesn't care because
49 : : * it only examines the dumpId field, but someday we might want to try harder.
50 : : */
51 : : char *
267 noah@leadboat.com 52 :CBC 124942 : sanitize_line(const char *str, bool want_hyphen)
53 : : {
54 : : char *result;
55 : : char *s;
56 : :
57 [ + + ]: 124942 : if (!str)
58 [ + + ]: 4811 : return pg_strdup(want_hyphen ? "-" : "");
59 : :
60 : 120131 : result = pg_strdup(str);
61 : :
62 [ + + ]: 1440797 : for (s = result; *s != '\0'; s++)
63 : : {
64 [ + + - + ]: 1320666 : if (*s == '\n' || *s == '\r')
65 : 190 : *s = ' ';
66 : : }
67 : :
68 : 120131 : return result;
69 : : }
70 : :
71 : :
72 : : /*
73 : : * Build GRANT/REVOKE command(s) for an object.
74 : : *
75 : : * name: the object name, in the form to use in the commands (already quoted)
76 : : * subname: the sub-object name, if any (already quoted); NULL if none
77 : : * nspname: the namespace the object is in (NULL if none); not pre-quoted
78 : : * type: the object type (as seen in GRANT command: must be one of
79 : : * TABLE, SEQUENCE, FUNCTION, PROCEDURE, LANGUAGE, SCHEMA, DATABASE, TABLESPACE,
80 : : * FOREIGN DATA WRAPPER, SERVER, PARAMETER or LARGE OBJECT)
81 : : * acls: the ACL string fetched from the database
82 : : * baseacls: the initial ACL string for this object
83 : : * owner: username of object owner (will be passed through fmtId); can be
84 : : * NULL or empty string to indicate "no owner known"
85 : : * prefix: string to prefix to each generated command; typically empty
86 : : * remoteVersion: version of database
87 : : *
88 : : * Returns true if okay, false if could not parse the acl string.
89 : : * The resulting commands (if any) are appended to the contents of 'sql'.
90 : : *
91 : : * baseacls is typically the result of acldefault() for the object's type
92 : : * and owner. However, if there is a pg_init_privs entry for the object,
93 : : * it should instead be the initprivs ACLs. When acls is itself a
94 : : * pg_init_privs entry, baseacls is what to dump that relative to; then
95 : : * it can be either an acldefault() value or an empty ACL "{}".
96 : : *
97 : : * Note: when processing a default ACL, prefix is "ALTER DEFAULT PRIVILEGES "
98 : : * or something similar, and name is an empty string.
99 : : *
100 : : * Note: beware of passing a fmtId() result directly as 'name' or 'subname',
101 : : * since this routine uses fmtId() internally.
102 : : */
103 : : bool
2990 tgl@sss.pgh.pa.us 104 : 44053 : buildACLCommands(const char *name, const char *subname, const char *nspname,
105 : : const char *type, const char *acls, const char *baseacls,
106 : : const char *owner, const char *prefix, int remoteVersion,
107 : : PQExpBuffer sql)
108 : : {
4101 109 : 44053 : bool ok = true;
3681 sfrost@snowman.net 110 : 44053 : char **aclitems = NULL;
1611 tgl@sss.pgh.pa.us 111 : 44053 : char **baseitems = NULL;
112 : 44053 : char **grantitems = NULL;
113 : 44053 : char **revokeitems = NULL;
3681 sfrost@snowman.net 114 : 44053 : int naclitems = 0;
1611 tgl@sss.pgh.pa.us 115 : 44053 : int nbaseitems = 0;
116 : 44053 : int ngrantitems = 0;
117 : 44053 : int nrevokeitems = 0;
118 : : int i;
119 : : PQExpBuffer grantee,
120 : : grantor,
121 : : privs,
122 : : privswgo;
123 : : PQExpBuffer firstsql,
124 : : secondsql;
125 : :
126 : : /*
127 : : * If the acl was NULL (initial default state), we need do nothing. Note
128 : : * that this is distinguishable from all-privileges-revoked, which will
129 : : * look like an empty array ("{}").
130 : : */
131 [ + - + + ]: 44053 : if (acls == NULL || *acls == '\0')
8376 132 : 189 : return true; /* object has default permissions */
133 : :
134 : : /* treat empty-string owner same as NULL */
7458 135 [ + - - + ]: 43864 : if (owner && *owner == '\0')
7458 tgl@sss.pgh.pa.us 136 :UBC 0 : owner = NULL;
137 : :
138 : : /* Parse the acls array */
1611 tgl@sss.pgh.pa.us 139 [ - + ]:CBC 43864 : if (!parsePGArray(acls, &aclitems, &naclitems))
140 : : {
1419 peter@eisentraut.org 141 :UBC 0 : free(aclitems);
1611 tgl@sss.pgh.pa.us 142 : 0 : return false;
143 : : }
144 : :
145 : : /* Parse the baseacls too */
1601 tgl@sss.pgh.pa.us 146 [ - + ]:CBC 43864 : if (!parsePGArray(baseacls, &baseitems, &nbaseitems))
147 : : {
1419 peter@eisentraut.org 148 :UBC 0 : free(aclitems);
149 : 0 : free(baseitems);
1601 tgl@sss.pgh.pa.us 150 : 0 : return false;
151 : : }
152 : :
153 : : /*
154 : : * Compare the actual ACL with the base ACL, extracting the privileges
155 : : * that need to be granted (i.e., are in the actual ACL but not the base
156 : : * ACL) and the ones that need to be revoked (the reverse). We use plain
157 : : * string comparisons to check for matches. In principle that could be
158 : : * fooled by extraneous issues such as whitespace, but since all these
159 : : * strings are the work of aclitemout(), it should be OK in practice.
160 : : * Besides, a false mismatch will just cause the output to be a little
161 : : * more verbose than it really needed to be.
162 : : */
81 michael@paquier.xyz 163 :GNC 43864 : grantitems = pg_malloc_array(char *, naclitems);
1611 tgl@sss.pgh.pa.us 164 [ + + ]:CBC 122695 : for (i = 0; i < naclitems; i++)
165 : : {
166 : 78831 : bool found = false;
167 : :
168 [ + + ]: 114360 : for (int j = 0; j < nbaseitems; j++)
169 : : {
170 [ + + ]: 112464 : if (strcmp(aclitems[i], baseitems[j]) == 0)
171 : : {
172 : 76935 : found = true;
173 : 76935 : break;
174 : : }
175 : : }
176 [ + + ]: 78831 : if (!found)
177 : 1896 : grantitems[ngrantitems++] = aclitems[i];
178 : : }
81 michael@paquier.xyz 179 :GNC 43864 : revokeitems = pg_malloc_array(char *, nbaseitems);
1611 tgl@sss.pgh.pa.us 180 [ + + ]:CBC 121200 : for (i = 0; i < nbaseitems; i++)
181 : : {
182 : 77336 : bool found = false;
183 : :
184 [ + + ]: 112126 : for (int j = 0; j < naclitems; j++)
185 : : {
186 [ + + ]: 111725 : if (strcmp(baseitems[i], aclitems[j]) == 0)
187 : : {
188 : 76935 : found = true;
189 : 76935 : break;
190 : : }
191 : : }
192 [ + + ]: 77336 : if (!found)
193 : 401 : revokeitems[nrevokeitems++] = baseitems[i];
194 : : }
195 : :
196 : : /* Prepare working buffers */
8376 197 : 43864 : grantee = createPQExpBuffer();
198 : 43864 : grantor = createPQExpBuffer();
199 : 43864 : privs = createPQExpBuffer();
200 : 43864 : privswgo = createPQExpBuffer();
201 : :
202 : : /*
203 : : * At the end, these two will be pasted together to form the result.
204 : : */
8321 peter_e@gmx.net 205 : 43864 : firstsql = createPQExpBuffer();
206 : 43864 : secondsql = createPQExpBuffer();
207 : :
208 : : /*
209 : : * Build REVOKE statements for ACLs listed in revokeitems[].
210 : : */
1598 tgl@sss.pgh.pa.us 211 [ + + ]: 44265 : for (i = 0; i < nrevokeitems; i++)
212 : : {
213 [ - + ]: 401 : if (!parseAclItem(revokeitems[i],
214 : : type, name, subname, remoteVersion,
215 : : grantee, grantor, privs, NULL))
216 : : {
1598 tgl@sss.pgh.pa.us 217 :UBC 0 : ok = false;
218 : 0 : break;
219 : : }
220 : :
1598 tgl@sss.pgh.pa.us 221 [ + - ]:CBC 401 : if (privs->len > 0)
222 : : {
223 : 401 : appendPQExpBuffer(firstsql, "%sREVOKE %s ON %s ",
224 : : prefix, privs->data, type);
225 [ + + + - ]: 401 : if (nspname && *nspname)
226 : 236 : appendPQExpBuffer(firstsql, "%s.", fmtId(nspname));
1240 jdavis@postgresql.or 227 [ + - + + ]: 401 : if (name && *name)
228 : 331 : appendPQExpBuffer(firstsql, "%s ", name);
229 : 401 : appendPQExpBufferStr(firstsql, "FROM ");
1598 tgl@sss.pgh.pa.us 230 [ + + ]: 401 : if (grantee->len == 0)
231 : 191 : appendPQExpBufferStr(firstsql, "PUBLIC;\n");
232 : : else
233 : 210 : appendPQExpBuffer(firstsql, "%s;\n",
234 : 210 : fmtId(grantee->data));
235 : : }
236 : : }
237 : :
238 : : /*
239 : : * At this point we have issued REVOKE statements for all initial and
240 : : * default privileges that are no longer present on the object, so we are
241 : : * almost ready to GRANT the privileges listed in grantitems[].
242 : : *
243 : : * We still need some hacking though to cover the case where new default
244 : : * public privileges are added in new versions: the REVOKE ALL will revoke
245 : : * them, leading to behavior different from what the old version had,
246 : : * which is generally not what's wanted. So add back default privs if the
247 : : * source database is too old to have had that particular priv. (As of
248 : : * right now, no such cases exist in supported versions.)
249 : : */
250 : :
251 : : /*
252 : : * Scan individual ACL items to be granted.
253 : : *
254 : : * The order in which privileges appear in the ACL string (the order they
255 : : * have been GRANT'd in, which the backend maintains) must be preserved to
256 : : * ensure that GRANTs WITH GRANT OPTION and subsequent GRANTs based on
257 : : * those are dumped in the correct order. However, some old server
258 : : * versions will show grants to PUBLIC before the owner's own grants; for
259 : : * consistency's sake, force the owner's grants to be output first.
260 : : */
1611 261 [ + + ]: 45760 : for (i = 0; i < ngrantitems; i++)
262 : : {
263 [ + - ]: 1896 : if (parseAclItem(grantitems[i], type, name, subname, remoteVersion,
264 : : grantee, grantor, privs, privswgo))
265 : : {
266 : : /*
267 : : * If the grantor isn't the owner, we'll need to use SET SESSION
268 : : * AUTHORIZATION to become the grantor. Issue the SET/RESET only
269 : : * if there's something useful to do.
270 : : */
271 [ + + + - ]: 1896 : if (privs->len > 0 || privswgo->len > 0)
272 : : {
273 : : PQExpBuffer thissql;
274 : :
275 : : /* Set owner as grantor if that's not explicit in the ACL */
276 [ - + - - ]: 1896 : if (grantor->len == 0 && owner)
1611 tgl@sss.pgh.pa.us 277 :UBC 0 : printfPQExpBuffer(grantor, "%s", owner);
278 : :
279 : : /* Make sure owner's own grants are output before others */
1611 tgl@sss.pgh.pa.us 280 [ + - ]:CBC 1896 : if (owner &&
281 [ + + ]: 1896 : strcmp(grantee->data, owner) == 0 &&
282 [ + - ]: 154 : strcmp(grantor->data, owner) == 0)
283 : 154 : thissql = firstsql;
284 : : else
285 : 1742 : thissql = secondsql;
286 : :
8321 peter_e@gmx.net 287 [ + - ]: 1896 : if (grantor->len > 0
288 [ + - - + ]: 1896 : && (!owner || strcmp(owner, grantor->data) != 0))
1611 tgl@sss.pgh.pa.us 289 :UBC 0 : appendPQExpBuffer(thissql, "SET SESSION AUTHORIZATION %s;\n",
8321 peter_e@gmx.net 290 : 0 : fmtId(grantor->data));
291 : :
8376 tgl@sss.pgh.pa.us 292 [ + + ]:CBC 1896 : if (privs->len > 0)
293 : : {
1611 294 : 1894 : appendPQExpBuffer(thissql, "%sGRANT %s ON %s ",
295 : : prefix, privs->data, type);
2990 296 [ + + + - ]: 1894 : if (nspname && *nspname)
1611 297 : 1598 : appendPQExpBuffer(thissql, "%s.", fmtId(nspname));
1240 jdavis@postgresql.or 298 [ + - + + ]: 1894 : if (name && *name)
299 : 1802 : appendPQExpBuffer(thissql, "%s ", name);
300 : 1894 : appendPQExpBufferStr(thissql, "TO ");
8376 tgl@sss.pgh.pa.us 301 [ + + ]: 1894 : if (grantee->len == 0)
1611 302 : 1051 : appendPQExpBufferStr(thissql, "PUBLIC;\n");
303 : : else
304 : 843 : appendPQExpBuffer(thissql, "%s;\n", fmtId(grantee->data));
305 : : }
8376 306 [ + + ]: 1896 : if (privswgo->len > 0)
307 : : {
1611 308 : 20 : appendPQExpBuffer(thissql, "%sGRANT %s ON %s ",
309 : : prefix, privswgo->data, type);
2990 310 [ + + + - ]: 20 : if (nspname && *nspname)
1611 311 : 19 : appendPQExpBuffer(thissql, "%s.", fmtId(nspname));
1240 jdavis@postgresql.or 312 [ + - + - ]: 20 : if (name && *name)
313 : 20 : appendPQExpBuffer(thissql, "%s ", name);
314 : 20 : appendPQExpBufferStr(thissql, "TO ");
8376 tgl@sss.pgh.pa.us 315 [ - + ]: 20 : if (grantee->len == 0)
1611 tgl@sss.pgh.pa.us 316 :UBC 0 : appendPQExpBufferStr(thissql, "PUBLIC");
317 : : else
1611 tgl@sss.pgh.pa.us 318 :CBC 20 : appendPQExpBufferStr(thissql, fmtId(grantee->data));
319 : 20 : appendPQExpBufferStr(thissql, " WITH GRANT OPTION;\n");
320 : : }
321 : :
8321 peter_e@gmx.net 322 [ + - ]: 1896 : if (grantor->len > 0
323 [ + - - + ]: 1896 : && (!owner || strcmp(owner, grantor->data) != 0))
1611 tgl@sss.pgh.pa.us 324 :UBC 0 : appendPQExpBufferStr(thissql, "RESET SESSION AUTHORIZATION;\n");
325 : : }
326 : : }
327 : : else
328 : : {
329 : : /* parseAclItem failed, give up */
330 : 0 : ok = false;
331 : 0 : break;
332 : : }
333 : : }
334 : :
8376 tgl@sss.pgh.pa.us 335 :CBC 43864 : destroyPQExpBuffer(grantee);
336 : 43864 : destroyPQExpBuffer(grantor);
337 : 43864 : destroyPQExpBuffer(privs);
338 : 43864 : destroyPQExpBuffer(privswgo);
339 : :
8321 peter_e@gmx.net 340 : 43864 : appendPQExpBuffer(sql, "%s%s", firstsql->data, secondsql->data);
341 : 43864 : destroyPQExpBuffer(firstsql);
342 : 43864 : destroyPQExpBuffer(secondsql);
343 : :
1419 peter@eisentraut.org 344 : 43864 : free(aclitems);
345 : 43864 : free(baseitems);
346 : 43864 : free(grantitems);
347 : 43864 : free(revokeitems);
348 : :
4101 tgl@sss.pgh.pa.us 349 : 43864 : return ok;
350 : : }
351 : :
352 : : /*
353 : : * Build ALTER DEFAULT PRIVILEGES command(s) for a single pg_default_acl entry.
354 : : *
355 : : * type: the object type (TABLES, FUNCTIONS, etc)
356 : : * nspname: schema name, or NULL for global default privileges
357 : : * acls: the ACL string fetched from the database
358 : : * acldefault: the appropriate default ACL for the object type and owner
359 : : * owner: username of privileges owner (will be passed through fmtId)
360 : : * remoteVersion: version of database
361 : : *
362 : : * Returns true if okay, false if could not parse the acl string.
363 : : * The resulting commands (if any) are appended to the contents of 'sql'.
364 : : */
365 : : bool
6056 366 : 132 : buildDefaultACLCommands(const char *type, const char *nspname,
367 : : const char *acls, const char *acldefault,
368 : : const char *owner,
369 : : int remoteVersion,
370 : : PQExpBuffer sql)
371 : : {
372 : : PQExpBuffer prefix;
373 : :
374 : 132 : prefix = createPQExpBuffer();
375 : :
376 : : /*
377 : : * We incorporate the target role directly into the command, rather than
378 : : * playing around with SET ROLE or anything like that. This is so that a
379 : : * permissions error leads to nothing happening, rather than changing
380 : : * default privileges for the wrong user.
381 : : */
382 : 132 : appendPQExpBuffer(prefix, "ALTER DEFAULT PRIVILEGES FOR ROLE %s ",
383 : : fmtId(owner));
384 [ + + ]: 132 : if (nspname)
385 : 62 : appendPQExpBuffer(prefix, "IN SCHEMA %s ", fmtId(nspname));
386 : :
387 : : /*
388 : : * There's no such thing as initprivs for a default ACL, so the base ACL
389 : : * is always just the object-type-specific default.
390 : : */
2990 391 [ - + ]: 132 : if (!buildACLCommands("", NULL, NULL, type,
392 : : acls, acldefault, owner,
3381 sfrost@snowman.net 393 : 132 : prefix->data, remoteVersion, sql))
394 : : {
3286 sfrost@snowman.net 395 :UBC 0 : destroyPQExpBuffer(prefix);
3381 396 : 0 : return false;
397 : : }
398 : :
6056 tgl@sss.pgh.pa.us 399 :CBC 132 : destroyPQExpBuffer(prefix);
400 : :
3381 sfrost@snowman.net 401 : 132 : return true;
402 : : }
403 : :
404 : : /*
405 : : * This will parse an aclitem string, having the general form
406 : : * username=privilegecodes/grantor
407 : : *
408 : : * Returns true on success, false on parse error. On success, the components
409 : : * of the string are returned in the PQExpBuffer parameters.
410 : : *
411 : : * The returned grantee string will be the dequoted username, or an empty
412 : : * string in the case of a grant to PUBLIC. The returned grantor is the
413 : : * dequoted grantor name. Privilege characters are translated to GRANT/REVOKE
414 : : * comma-separated privileges lists. If "privswgo" is non-NULL, the result is
415 : : * separate lists for privileges with grant option ("privswgo") and without
416 : : * ("privs"). Otherwise, "privs" bears every relevant privilege, ignoring the
417 : : * grant option distinction.
418 : : *
419 : : * Note: for cross-version compatibility, it's important to use ALL to
420 : : * represent the privilege sets whenever appropriate.
421 : : */
422 : : static bool
6312 tgl@sss.pgh.pa.us 423 : 2297 : parseAclItem(const char *item, const char *type,
424 : : const char *name, const char *subname, int remoteVersion,
425 : : PQExpBuffer grantee, PQExpBuffer grantor,
426 : : PQExpBuffer privs, PQExpBuffer privswgo)
427 : : {
428 : : char *buf;
8376 429 : 2297 : bool all_with_go = true;
430 : 2297 : bool all_without_go = true;
431 : : char *eqpos;
432 : : char *slpos;
433 : : char *pos;
434 : :
2558 michael@paquier.xyz 435 : 2297 : buf = pg_strdup(item);
436 : :
437 : : /* user or group name is string up to = */
1611 tgl@sss.pgh.pa.us 438 : 2297 : eqpos = dequoteAclUserName(grantee, buf);
8314 439 [ - + ]: 2297 : if (*eqpos != '=')
440 : : {
2558 michael@paquier.xyz 441 :UBC 0 : pg_free(buf);
8376 tgl@sss.pgh.pa.us 442 : 0 : return false;
443 : : }
444 : :
445 : : /* grantor should appear after / */
8376 tgl@sss.pgh.pa.us 446 :CBC 2297 : slpos = strchr(eqpos + 1, '/');
447 [ + - ]: 2297 : if (slpos)
448 : : {
8314 449 : 2297 : *slpos++ = '\0';
1611 450 : 2297 : slpos = dequoteAclUserName(grantor, slpos);
8314 451 [ - + ]: 2297 : if (*slpos != '\0')
452 : : {
2558 michael@paquier.xyz 453 :UBC 0 : pg_free(buf);
8314 tgl@sss.pgh.pa.us 454 : 0 : return false;
455 : : }
456 : : }
457 : : else
458 : : {
2558 michael@paquier.xyz 459 : 0 : pg_free(buf);
3492 tgl@sss.pgh.pa.us 460 : 0 : return false;
461 : : }
462 : :
463 : : /* privilege codes */
464 : : #define CONVERT_PRIV(code, keywd) \
465 : : do { \
466 : : if ((pos = strchr(eqpos + 1, code))) \
467 : : { \
468 : : if (*(pos + 1) == '*' && privswgo != NULL) \
469 : : { \
470 : : AddAcl(privswgo, keywd, subname); \
471 : : all_without_go = false; \
472 : : } \
473 : : else \
474 : : { \
475 : : AddAcl(privs, keywd, subname); \
476 : : all_with_go = false; \
477 : : } \
478 : : } \
479 : : else \
480 : : all_with_go = all_without_go = false; \
481 : : } while (0)
482 : :
8376 tgl@sss.pgh.pa.us 483 :CBC 2297 : resetPQExpBuffer(privs);
484 : 2297 : resetPQExpBuffer(privswgo);
485 : :
6049 486 [ + + + + ]: 2297 : if (strcmp(type, "TABLE") == 0 || strcmp(type, "SEQUENCE") == 0 ||
487 [ + + - + ]: 851 : strcmp(type, "TABLES") == 0 || strcmp(type, "SEQUENCES") == 0)
488 : : {
8376 489 [ + + - + : 1537 : CONVERT_PRIV('r', "SELECT");
- - ]
490 : :
6049 491 [ + + ]: 1537 : if (strcmp(type, "SEQUENCE") == 0 ||
492 [ - + ]: 1473 : strcmp(type, "SEQUENCES") == 0)
493 : : /* sequence only */
7409 bruce@momjian.us 494 [ + + + + : 127 : CONVERT_PRIV('U', "USAGE");
+ + ]
495 : : else
496 : : {
497 : : /* table only */
498 [ + + - + : 1473 : CONVERT_PRIV('a', "INSERT");
- - ]
3492 tgl@sss.pgh.pa.us 499 [ + + - + : 1473 : CONVERT_PRIV('x', "REFERENCES");
- - ]
500 : : /* rest are not applicable to columns */
6312 501 [ + + ]: 1473 : if (subname == NULL)
502 : : {
3492 503 [ + + - + : 425 : CONVERT_PRIV('d', "DELETE");
- - ]
504 [ + + - + : 425 : CONVERT_PRIV('t', "TRIGGER");
- - ]
1603 505 [ + + - + : 425 : CONVERT_PRIV('D', "TRUNCATE");
- - ]
783 nathan@postgresql.or 506 [ + + - + : 425 : CONVERT_PRIV('m', "MAINTAIN");
- - ]
507 : : }
508 : : }
509 : :
510 : : /* UPDATE */
3492 tgl@sss.pgh.pa.us 511 [ + + + + : 1660 : CONVERT_PRIV('w', "UPDATE");
+ + ]
512 : : }
50 peter@eisentraut.org 513 [ + + ]:GNC 760 : else if (strcmp(type, "PROPERTY GRAPH") == 0 ||
514 [ - + ]: 652 : strcmp(type, "PROPERTY GRAPHS") == 0)
515 [ + - - + : 216 : CONVERT_PRIV('r', "SELECT");
- - ]
6049 tgl@sss.pgh.pa.us 516 [ + + ]:CBC 652 : else if (strcmp(type, "FUNCTION") == 0 ||
517 [ + + ]: 496 : strcmp(type, "FUNCTIONS") == 0)
8376 518 [ + - + + : 434 : CONVERT_PRIV('X', "EXECUTE");
+ + ]
3078 peter_e@gmx.net 519 [ + - ]: 435 : else if (strcmp(type, "PROCEDURE") == 0 ||
520 [ - + ]: 435 : strcmp(type, "PROCEDURES") == 0)
3078 peter_e@gmx.net 521 [ # # # # :UBC 0 : CONVERT_PRIV('X', "EXECUTE");
# # ]
8376 tgl@sss.pgh.pa.us 522 [ + + ]:CBC 435 : else if (strcmp(type, "LANGUAGE") == 0)
523 [ + - - + : 37 : CONVERT_PRIV('U', "USAGE");
- - ]
3325 teodor@sigaev.ru 524 [ + + ]: 398 : else if (strcmp(type, "SCHEMA") == 0 ||
3276 tgl@sss.pgh.pa.us 525 [ - + ]: 287 : strcmp(type, "SCHEMAS") == 0)
526 : : {
8376 527 [ + + - + : 111 : CONVERT_PRIV('C', "CREATE");
- - ]
528 [ + - - + : 222 : CONVERT_PRIV('U', "USAGE");
- - ]
529 : : }
530 [ + + ]: 287 : else if (strcmp(type, "DATABASE") == 0)
531 : : {
532 [ + + - + : 57 : CONVERT_PRIV('C', "CREATE");
- - ]
7310 533 [ + + - + : 57 : CONVERT_PRIV('c', "CONNECT");
- - ]
8376 534 [ + + - + : 57 : CONVERT_PRIV('T', "TEMPORARY");
- - ]
535 : : }
7991 536 [ - + ]: 230 : else if (strcmp(type, "TABLESPACE") == 0)
7991 tgl@sss.pgh.pa.us 537 [ # # # # :UBC 0 : CONVERT_PRIV('C', "CREATE");
# # ]
4895 tgl@sss.pgh.pa.us 538 [ + + ]:CBC 230 : else if (strcmp(type, "TYPE") == 0 ||
539 [ + + ]: 105 : strcmp(type, "TYPES") == 0)
540 [ + - - + : 270 : CONVERT_PRIV('U', "USAGE");
- - ]
6346 peter_e@gmx.net 541 [ + + ]: 95 : else if (strcmp(type, "FOREIGN DATA WRAPPER") == 0)
542 [ + - - + : 30 : CONVERT_PRIV('U', "USAGE");
- - ]
5907 heikki.linnakangas@i 543 [ + + ]: 65 : else if (strcmp(type, "FOREIGN SERVER") == 0)
6346 peter_e@gmx.net 544 [ + - - + : 30 : CONVERT_PRIV('U', "USAGE");
- - ]
5603 rhaas@postgresql.org 545 [ - + ]: 35 : else if (strcmp(type, "FOREIGN TABLE") == 0)
5603 rhaas@postgresql.org 546 [ # # # # :UBC 0 : CONVERT_PRIV('r', "SELECT");
# # ]
1490 tgl@sss.pgh.pa.us 547 [ + + ]:CBC 35 : else if (strcmp(type, "PARAMETER") == 0)
548 : : {
549 [ + + + + : 3 : CONVERT_PRIV('s', "SET");
+ - ]
550 [ + - + + : 3 : CONVERT_PRIV('A', "ALTER SYSTEM");
+ - ]
551 : : }
396 fujii@postgresql.org 552 [ - + ]: 32 : else if (strcmp(type, "LARGE OBJECT") == 0 ||
396 fujii@postgresql.org 553 [ # # ]:UBC 0 : strcmp(type, "LARGE OBJECTS") == 0)
554 : : {
5989 itagaki.takahiro@gma 555 [ + - - + :CBC 32 : CONVERT_PRIV('r', "SELECT");
- - ]
556 [ + - - + : 64 : CONVERT_PRIV('w', "UPDATE");
- - ]
557 : : }
558 : : else
8376 tgl@sss.pgh.pa.us 559 :UBC 0 : abort();
560 : :
561 : : #undef CONVERT_PRIV
562 : :
8376 tgl@sss.pgh.pa.us 563 [ + + ]:CBC 2297 : if (all_with_go)
564 : : {
565 : 2 : resetPQExpBuffer(privs);
566 : 2 : printfPQExpBuffer(privswgo, "ALL");
6312 567 [ - + ]: 2 : if (subname)
6312 tgl@sss.pgh.pa.us 568 :UBC 0 : appendPQExpBuffer(privswgo, "(%s)", subname);
569 : : }
8376 tgl@sss.pgh.pa.us 570 [ + + ]:CBC 2295 : else if (all_without_go)
571 : : {
572 : 708 : resetPQExpBuffer(privswgo);
573 : 708 : printfPQExpBuffer(privs, "ALL");
6312 574 [ - + ]: 708 : if (subname)
6312 tgl@sss.pgh.pa.us 575 :UBC 0 : appendPQExpBuffer(privs, "(%s)", subname);
576 : : }
577 : :
2558 michael@paquier.xyz 578 :CBC 2297 : pg_free(buf);
579 : :
8376 tgl@sss.pgh.pa.us 580 : 2297 : return true;
581 : : }
582 : :
583 : : /*
584 : : * Transfer the role name at *input into the output buffer, adding
585 : : * quoting according to the same rules as putid() in backend's acl.c.
586 : : */
587 : : void
1611 588 : 777 : quoteAclUserName(PQExpBuffer output, const char *input)
589 : : {
590 : : const char *src;
591 : 777 : bool safe = true;
592 : :
593 [ + + ]: 13536 : for (src = input; *src; src++)
594 : : {
595 : : /* This test had better match what putid() does */
596 [ + + + + ]: 12909 : if (!isalnum((unsigned char) *src) && *src != '_')
597 : : {
598 : 150 : safe = false;
599 : 150 : break;
600 : : }
601 : : }
602 [ + + ]: 777 : if (!safe)
603 : 150 : appendPQExpBufferChar(output, '"');
604 [ + + ]: 14886 : for (src = input; *src; src++)
605 : : {
606 : : /* A double quote character in a username is encoded as "" */
607 [ + + ]: 14109 : if (*src == '"')
608 : 150 : appendPQExpBufferChar(output, '"');
609 : 14109 : appendPQExpBufferChar(output, *src);
610 : : }
611 [ + + ]: 777 : if (!safe)
612 : 150 : appendPQExpBufferChar(output, '"');
613 : 777 : }
614 : :
615 : : /*
616 : : * Transfer a user or group name starting at *input into the output buffer,
617 : : * dequoting if needed. Returns a pointer to just past the input name.
618 : : * The name is taken to end at an unquoted '=' or end of string.
619 : : * Note: unlike quoteAclUserName(), this first clears the output buffer.
620 : : */
621 : : static char *
622 : 4594 : dequoteAclUserName(PQExpBuffer output, char *input)
623 : : {
8314 624 : 4594 : resetPQExpBuffer(output);
625 : :
626 [ + + + + ]: 39318 : while (*input && *input != '=')
627 : : {
628 : : /*
629 : : * If user name isn't quoted, then just add it to the output buffer
630 : : */
631 [ + + ]: 34724 : if (*input != '"')
632 : 34633 : appendPQExpBufferChar(output, *input++);
633 : : else
634 : : {
635 : : /* Otherwise, it's a quoted username */
636 : 91 : input++;
637 : : /* Loop until we come across an unescaped quote */
8300 638 [ + + + + ]: 2184 : while (!(*input == '"' && *(input + 1) != '"'))
639 : : {
8314 640 [ - + ]: 2093 : if (*input == '\0')
3240 tgl@sss.pgh.pa.us 641 :UBC 0 : return input; /* really a syntax error... */
642 : :
643 : : /*
644 : : * Quoting convention is to escape " as "". Keep this code in
645 : : * sync with putid() in backend's acl.c.
646 : : */
8300 tgl@sss.pgh.pa.us 647 [ + + + - ]:CBC 2093 : if (*input == '"' && *(input + 1) == '"')
648 : 91 : input++;
8314 649 : 2093 : appendPQExpBufferChar(output, *input++);
650 : : }
651 : 91 : input++;
652 : : }
653 : : }
654 : 4594 : return input;
655 : : }
656 : :
657 : : /*
658 : : * Append a privilege keyword to a keyword list, inserting comma if needed.
659 : : */
660 : : static void
6312 661 : 3018 : AddAcl(PQExpBuffer aclbuf, const char *keyword, const char *subname)
662 : : {
8376 663 [ + + ]: 3018 : if (aclbuf->len > 0)
664 : 703 : appendPQExpBufferChar(aclbuf, ',');
4551 heikki.linnakangas@i 665 : 3018 : appendPQExpBufferStr(aclbuf, keyword);
6312 tgl@sss.pgh.pa.us 666 [ + + ]: 3018 : if (subname)
667 : 1048 : appendPQExpBuffer(aclbuf, "(%s)", subname);
8376 668 : 3018 : }
669 : :
670 : :
671 : : /*
672 : : * buildShSecLabelQuery
673 : : *
674 : : * Build a query to retrieve security labels for a shared object.
675 : : * The object is identified by its OID plus the name of the catalog
676 : : * it can be found in (e.g., "pg_database" for database names).
677 : : * The query is appended to "sql". (We don't execute it here so as to
678 : : * keep this file free of assumptions about how to deal with SQL errors.)
679 : : */
680 : : void
2079 peter@eisentraut.org 681 : 401 : buildShSecLabelQuery(const char *catalog_name, Oid objectId,
682 : : PQExpBuffer sql)
683 : : {
5403 rhaas@postgresql.org 684 : 401 : appendPQExpBuffer(sql,
685 : : "SELECT provider, label FROM pg_catalog.pg_shseclabel "
686 : : "WHERE classoid = 'pg_catalog.%s'::pg_catalog.regclass "
687 : : "AND objoid = '%u'", catalog_name, objectId);
688 : 401 : }
689 : :
690 : : /*
691 : : * emitShSecLabels
692 : : *
693 : : * Construct SECURITY LABEL commands using the data retrieved by the query
694 : : * generated by buildShSecLabelQuery, and append them to "buffer".
695 : : * Here, the target object is identified by its type name (e.g. "DATABASE")
696 : : * and its name (not pre-quoted).
697 : : */
698 : : void
699 : 401 : emitShSecLabels(PGconn *conn, PGresult *res, PQExpBuffer buffer,
700 : : const char *objtype, const char *objname)
701 : : {
702 : : int i;
703 : :
704 [ - + ]: 401 : for (i = 0; i < PQntuples(res); i++)
705 : : {
5077 bruce@momjian.us 706 :UBC 0 : char *provider = PQgetvalue(res, i, 0);
707 : 0 : char *label = PQgetvalue(res, i, 1);
708 : :
709 : : /* must use fmtId result before calling it again */
5403 rhaas@postgresql.org 710 : 0 : appendPQExpBuffer(buffer,
711 : : "SECURITY LABEL FOR %s ON %s",
712 : : fmtId(provider), objtype);
713 : 0 : appendPQExpBuffer(buffer,
714 : : " %s IS ",
715 : : fmtId(objname));
716 : 0 : appendStringLiteralConn(buffer, label, conn);
4551 heikki.linnakangas@i 717 : 0 : appendPQExpBufferStr(buffer, ";\n");
718 : : }
5403 rhaas@postgresql.org 719 :CBC 401 : }
720 : :
721 : :
722 : : /*
723 : : * Detect whether the given GUC variable is of GUC_LIST_QUOTE type.
724 : : *
725 : : * It'd be better if we could inquire this directly from the backend; but even
726 : : * if there were a function for that, it could only tell us about variables
727 : : * currently known to guc.c, so that it'd be unsafe for extensions to declare
728 : : * GUC_LIST_QUOTE variables anyway. Lacking a solution for that, it doesn't
729 : : * seem worth the work to do more than have this list, which must be kept in
730 : : * sync with the variables actually marked GUC_LIST_QUOTE in guc_parameters.dat.
731 : : */
732 : : bool
2967 tgl@sss.pgh.pa.us 733 : 70 : variable_is_guc_list_quote(const char *name)
734 : : {
2005 michael@paquier.xyz 735 [ + + + - ]: 135 : if (pg_strcasecmp(name, "local_preload_libraries") == 0 ||
129 dgustafsson@postgres 736 [ + + ]: 130 : pg_strcasecmp(name, "oauth_validator_libraries") == 0 ||
2005 michael@paquier.xyz 737 [ + - ]: 125 : pg_strcasecmp(name, "search_path") == 0 ||
2967 tgl@sss.pgh.pa.us 738 [ + - ]: 120 : pg_strcasecmp(name, "session_preload_libraries") == 0 ||
739 [ + + ]: 120 : pg_strcasecmp(name, "shared_preload_libraries") == 0 ||
2005 michael@paquier.xyz 740 [ - + ]: 115 : pg_strcasecmp(name, "temp_tablespaces") == 0 ||
741 : 55 : pg_strcasecmp(name, "unix_socket_directories") == 0)
2967 tgl@sss.pgh.pa.us 742 : 15 : return true;
743 : : else
744 : 55 : return false;
745 : : }
746 : :
747 : : /*
748 : : * SplitGUCList --- parse a string containing identifiers or file names
749 : : *
750 : : * This is used to split the value of a GUC_LIST_QUOTE GUC variable, without
751 : : * presuming whether the elements will be taken as identifiers or file names.
752 : : * See comparable code in src/backend/utils/adt/varlena.c.
753 : : *
754 : : * Inputs:
755 : : * rawstring: the input string; must be overwritable! On return, it's
756 : : * been modified to contain the separated identifiers.
757 : : * separator: the separator punctuation expected between identifiers
758 : : * (typically '.' or ','). Whitespace may also appear around
759 : : * identifiers.
760 : : * Outputs:
761 : : * namelist: receives a malloc'd, null-terminated array of pointers to
762 : : * identifiers within rawstring. Caller should free this
763 : : * even on error return.
764 : : *
765 : : * Returns true if okay, false if there is a syntax error in the string.
766 : : */
767 : : bool
2835 768 : 15 : SplitGUCList(char *rawstring, char separator,
769 : : char ***namelist)
770 : : {
771 : 15 : char *nextp = rawstring;
772 : 15 : bool done = false;
773 : : char **nextptr;
774 : :
775 : : /*
776 : : * Since we disallow empty identifiers, this is a conservative
777 : : * overestimate of the number of pointers we could need. Allow one for
778 : : * list terminator.
779 : : */
81 michael@paquier.xyz 780 :GNC 15 : *namelist = nextptr =
781 : 15 : pg_malloc_array(char *, (strlen(rawstring) / 2 + 2));
2835 tgl@sss.pgh.pa.us 782 :CBC 15 : *nextptr = NULL;
783 : :
784 [ - + ]: 15 : while (isspace((unsigned char) *nextp))
2835 tgl@sss.pgh.pa.us 785 :UBC 0 : nextp++; /* skip leading whitespace */
786 : :
2835 tgl@sss.pgh.pa.us 787 [ + + ]:CBC 15 : if (*nextp == '\0')
182 tgl@sss.pgh.pa.us 788 :GNC 5 : return true; /* empty string represents empty list */
789 : :
790 : : /* At the top of the loop, we are at start of a new identifier. */
791 : : do
792 : : {
793 : : char *curname;
794 : : char *endp;
795 : :
2835 tgl@sss.pgh.pa.us 796 [ + + ]:CBC 25 : if (*nextp == '"')
797 : : {
798 : : /* Quoted name --- collapse quote-quote pairs */
799 : 20 : curname = nextp + 1;
800 : : for (;;)
801 : : {
802 : 30 : endp = strchr(nextp + 1, '"');
803 [ - + ]: 25 : if (endp == NULL)
2835 tgl@sss.pgh.pa.us 804 :UBC 0 : return false; /* mismatched quotes */
2835 tgl@sss.pgh.pa.us 805 [ + + ]:CBC 25 : if (endp[1] != '"')
806 : 20 : break; /* found end of quoted name */
807 : : /* Collapse adjacent quotes into one quote, and look again */
808 : 5 : memmove(endp, endp + 1, strlen(endp));
809 : 5 : nextp = endp;
810 : : }
811 : : /* endp now points at the terminating quote */
812 : 20 : nextp = endp + 1;
813 : : }
814 : : else
815 : : {
816 : : /* Unquoted name --- extends to separator or whitespace */
817 : 5 : curname = nextp;
818 [ + + + - ]: 55 : while (*nextp && *nextp != separator &&
819 [ + - ]: 50 : !isspace((unsigned char) *nextp))
820 : 50 : nextp++;
821 : 5 : endp = nextp;
822 [ - + ]: 5 : if (curname == nextp)
2835 tgl@sss.pgh.pa.us 823 :UBC 0 : return false; /* empty unquoted name not allowed */
824 : : }
825 : :
2835 tgl@sss.pgh.pa.us 826 [ - + ]:CBC 25 : while (isspace((unsigned char) *nextp))
2835 tgl@sss.pgh.pa.us 827 :UBC 0 : nextp++; /* skip trailing whitespace */
828 : :
2835 tgl@sss.pgh.pa.us 829 [ + + ]:CBC 25 : if (*nextp == separator)
830 : : {
831 : 15 : nextp++;
832 [ + + ]: 30 : while (isspace((unsigned char) *nextp))
833 : 15 : nextp++; /* skip leading whitespace for next */
834 : : /* we expect another name, so done remains false */
835 : : }
836 [ + - ]: 10 : else if (*nextp == '\0')
837 : 10 : done = true;
838 : : else
2835 tgl@sss.pgh.pa.us 839 :UBC 0 : return false; /* invalid syntax */
840 : :
841 : : /* Now safe to overwrite separator with a null */
2835 tgl@sss.pgh.pa.us 842 :CBC 25 : *endp = '\0';
843 : :
844 : : /*
845 : : * Finished isolating current name --- add it to output array
846 : : */
847 : 25 : *nextptr++ = curname;
848 : :
849 : : /* Loop back if we didn't reach end of string */
850 [ + + ]: 25 : } while (!done);
851 : :
852 : 10 : *nextptr = NULL;
853 : 10 : return true;
854 : : }
855 : :
856 : : /*
857 : : * Helper function for dumping "ALTER DATABASE/ROLE SET ..." commands.
858 : : *
859 : : * Parse the contents of configitem (a "name=value" string), wrap it in
860 : : * a complete ALTER command, and append it to buf.
861 : : *
862 : : * type is DATABASE or ROLE, and name is the name of the database or role.
863 : : * If we need an "IN" clause, type2 and name2 similarly define what to put
864 : : * there; otherwise they should be NULL.
865 : : * conn is used only to determine string-literal quoting conventions.
866 : : */
867 : : void
3025 868 : 30 : makeAlterConfigCommand(PGconn *conn, const char *configitem,
869 : : const char *type, const char *name,
870 : : const char *type2, const char *name2,
871 : : PQExpBuffer buf)
872 : : {
873 : : char *mine;
874 : : char *pos;
875 : :
876 : : /* Parse the configitem. If we can't find an "=", silently do nothing. */
877 : 30 : mine = pg_strdup(configitem);
878 : 30 : pos = strchr(mine, '=');
879 [ - + ]: 30 : if (pos == NULL)
880 : : {
3025 tgl@sss.pgh.pa.us 881 :UBC 0 : pg_free(mine);
882 : 0 : return;
883 : : }
3025 tgl@sss.pgh.pa.us 884 :CBC 30 : *pos++ = '\0';
885 : :
886 : : /* Build the command, with suitable quoting for everything. */
887 : 30 : appendPQExpBuffer(buf, "ALTER %s %s ", type, fmtId(name));
888 [ - + - - ]: 30 : if (type2 != NULL && name2 != NULL)
3025 tgl@sss.pgh.pa.us 889 :UBC 0 : appendPQExpBuffer(buf, "IN %s %s ", type2, fmtId(name2));
3025 tgl@sss.pgh.pa.us 890 :CBC 30 : appendPQExpBuffer(buf, "SET %s TO ", fmtId(mine));
891 : :
892 : : /*
893 : : * Variables that are marked GUC_LIST_QUOTE were already fully quoted by
894 : : * flatten_set_variable_args() before they were put into the setconfig
895 : : * array. However, because the quoting rules used there aren't exactly
896 : : * like SQL's, we have to break the list value apart and then quote the
897 : : * elements as string literals. (The elements may be double-quoted as-is,
898 : : * but we can't just feed them to the SQL parser; it would do the wrong
899 : : * thing with elements that are zero-length or longer than NAMEDATALEN.)
900 : : * Also, we need a special case for empty lists.
901 : : *
902 : : * Variables that are not so marked should just be emitted as simple
903 : : * string literals. If the variable is not known to
904 : : * variable_is_guc_list_quote(), we'll do that; this makes it unsafe to
905 : : * use GUC_LIST_QUOTE for extension variables.
906 : : */
2967 907 [ - + ]: 30 : if (variable_is_guc_list_quote(mine))
908 : : {
909 : : char **namelist;
910 : : char **nameptr;
911 : :
912 : : /* Parse string into list of identifiers */
913 : : /* this shouldn't fail really */
2835 tgl@sss.pgh.pa.us 914 [ # # ]:UBC 0 : if (SplitGUCList(pos, ',', &namelist))
915 : : {
916 : : /* Special case: represent an empty list as NULL */
182 tgl@sss.pgh.pa.us 917 [ # # ]:UNC 0 : if (*namelist == NULL)
918 : 0 : appendPQExpBufferStr(buf, "NULL");
2835 tgl@sss.pgh.pa.us 919 [ # # ]:UBC 0 : for (nameptr = namelist; *nameptr; nameptr++)
920 : : {
921 [ # # ]: 0 : if (nameptr != namelist)
922 : 0 : appendPQExpBufferStr(buf, ", ");
923 : 0 : appendStringLiteralConn(buf, *nameptr, conn);
924 : : }
925 : : }
926 : 0 : pg_free(namelist);
927 : : }
928 : : else
3025 tgl@sss.pgh.pa.us 929 :CBC 30 : appendStringLiteralConn(buf, pos, conn);
930 : :
931 : 30 : appendPQExpBufferStr(buf, ";\n");
932 : :
933 : 30 : pg_free(mine);
934 : : }
935 : :
936 : : /*
937 : : * create_or_open_dir
938 : : *
939 : : * This will create a new directory with the given dirname. If there is
940 : : * already an empty directory with that name, then use it.
941 : : */
942 : : void
390 andrew@dunslane.net 943 : 76 : create_or_open_dir(const char *dirname)
944 : : {
945 : : int ret;
946 : :
947 [ - + - - ]: 76 : switch ((ret = pg_check_dir(dirname)))
948 : : {
390 andrew@dunslane.net 949 :UBC 0 : case -1:
950 : : /* opendir failed but not with ENOENT */
951 : 0 : pg_fatal("could not open directory \"%s\": %m", dirname);
952 : : break;
390 andrew@dunslane.net 953 :CBC 76 : case 0:
954 : : /* directory does not exist */
955 [ - + ]: 76 : if (mkdir(dirname, pg_dir_create_mode) < 0)
390 andrew@dunslane.net 956 :UBC 0 : pg_fatal("could not create directory \"%s\": %m", dirname);
390 andrew@dunslane.net 957 :CBC 76 : break;
390 andrew@dunslane.net 958 :UBC 0 : case 1:
959 : : /* exists and is empty, fix perms */
960 [ # # ]: 0 : if (chmod(dirname, pg_dir_create_mode) != 0)
961 : 0 : pg_fatal("could not change permissions of directory \"%s\": %m",
962 : : dirname);
963 : 0 : break;
964 : 0 : default:
965 : : /* exists and is not empty */
966 : 0 : pg_fatal("directory \"%s\" is not empty", dirname);
967 : : }
390 andrew@dunslane.net 968 :CBC 76 : }
969 : :
970 : : /*
971 : : * Generates a valid restrict key (i.e., an alphanumeric string) for use with
972 : : * psql's \restrict and \unrestrict meta-commands. For safety, the value is
973 : : * chosen at random.
974 : : */
975 : : char *
267 nathan@postgresql.or 976 : 228 : generate_restrict_key(void)
977 : : {
978 : : uint8 buf[64];
979 : 228 : char *ret = palloc(sizeof(buf));
980 : :
981 [ - + ]: 228 : if (!pg_strong_random(buf, sizeof(buf)))
267 nathan@postgresql.or 982 :UBC 0 : return NULL;
983 : :
267 nathan@postgresql.or 984 [ + + ]:CBC 14592 : for (int i = 0; i < sizeof(buf) - 1; i++)
985 : : {
986 : 14364 : uint8 idx = buf[i] % strlen(restrict_chars);
987 : :
988 : 14364 : ret[i] = restrict_chars[idx];
989 : : }
990 : 228 : ret[sizeof(buf) - 1] = '\0';
991 : :
992 : 228 : return ret;
993 : : }
994 : :
995 : : /*
996 : : * Checks that a given restrict key (intended for use with psql's \restrict and
997 : : * \unrestrict meta-commands) contains only alphanumeric characters.
998 : : */
999 : : bool
1000 : 260 : valid_restrict_key(const char *restrict_key)
1001 : : {
1002 : 260 : return restrict_key != NULL &&
1003 [ + - + - ]: 520 : restrict_key[0] != '\0' &&
1004 [ + - ]: 260 : strspn(restrict_key, restrict_chars) == strlen(restrict_key);
1005 : : }
|