Get rid of some unnecessary dependencies on DataDir: wherever possible,

the backend should rely on its working-directory setting instead.
Also do some message-style police work in contrib/adminpack.
This commit is contained in:
Tom Lane 2006-11-06 03:06:41 +00:00
parent 62fe410ec6
commit 74686b6de7
3 changed files with 83 additions and 102 deletions

View File

@ -8,7 +8,7 @@
* Author: Andreas Pflug <pgadmin@pse-consulting.de> * Author: Andreas Pflug <pgadmin@pse-consulting.de>
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/contrib/adminpack/adminpack.c,v 1.7 2006/10/20 00:59:03 tgl Exp $ * $PostgreSQL: pgsql/contrib/adminpack/adminpack.c,v 1.8 2006/11/06 03:06:40 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -60,43 +60,47 @@ typedef struct
*/ */
/* /*
* Return an absolute path. Argument may be absolute or * Convert a "text" filename argument to C string, and check it's allowable.
* relative to the DataDir. *
* Filename may be absolute or relative to the DataDir, but we only allow
* absolute paths that match DataDir or Log_directory.
*/ */
static char * static char *
absClusterPath(text *arg, bool logAllowed) convert_and_check_filename(text *arg, bool logAllowed)
{ {
char *filename; int input_len = VARSIZE(arg) - VARHDRSZ;
int len = VARSIZE(arg) - VARHDRSZ; char *filename = palloc(input_len + 1);
int dlen = strlen(DataDir);
filename = palloc(len + 1); memcpy(filename, VARDATA(arg), input_len);
memcpy(filename, VARDATA(arg), len); filename[input_len] = '\0';
filename[len] = 0;
if (strstr(filename, "..") != NULL) canonicalize_path(filename); /* filename can change length here */
/* Disallow ".." in the path */
if (path_contains_parent_reference(filename))
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
(errmsg("No .. allowed in filenames")))); (errmsg("reference to parent directory (\"..\") not allowed"))));
if (is_absolute_path(filename)) if (is_absolute_path(filename))
{ {
if (logAllowed && !strncmp(filename, Log_directory, strlen(Log_directory))) /* Allow absolute references within DataDir */
if (path_is_prefix_of_path(DataDir, filename))
return filename;
/* The log directory might be outside our datadir, but allow it */
if (logAllowed &&
is_absolute_path(Log_directory) &&
path_is_prefix_of_path(Log_directory, filename))
return filename; return filename;
if (strncmp(filename, DataDir, dlen))
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
(errmsg("Absolute path not allowed"))));
return filename; ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
(errmsg("absolute path not allowed"))));
return NULL; /* keep compiler quiet */
} }
else else
{ {
char *absname = palloc(dlen + len + 2); return filename;
sprintf(absname, "%s/%s", DataDir, filename);
pfree(filename);
return absname;
} }
} }
@ -129,17 +133,17 @@ pg_file_write(PG_FUNCTION_ARGS)
requireSuperuser(); requireSuperuser();
filename = absClusterPath(PG_GETARG_TEXT_P(0), false); filename = convert_and_check_filename(PG_GETARG_TEXT_P(0), false);
data = PG_GETARG_TEXT_P(1); data = PG_GETARG_TEXT_P(1);
if (PG_ARGISNULL(2) || !PG_GETARG_BOOL(2)) if (!PG_GETARG_BOOL(2))
{ {
struct stat fst; struct stat fst;
if (stat(filename, &fst) >= 0) if (stat(filename, &fst) >= 0)
ereport(ERROR, ereport(ERROR,
(ERRCODE_DUPLICATE_FILE, (ERRCODE_DUPLICATE_FILE,
errmsg("file %s exists", filename))); errmsg("file \"%s\" exists", filename)));
f = fopen(filename, "wb"); f = fopen(filename, "wb");
} }
@ -147,11 +151,10 @@ pg_file_write(PG_FUNCTION_ARGS)
f = fopen(filename, "ab"); f = fopen(filename, "ab");
if (!f) if (!f)
{
ereport(ERROR, ereport(ERROR,
(errcode_for_file_access(), (errcode_for_file_access(),
errmsg("could open file %s for writing: %m", filename))); errmsg("could not open file \"%s\" for writing: %m",
} filename)));
if (VARSIZE(data) != 0) if (VARSIZE(data) != 0)
{ {
@ -160,7 +163,7 @@ pg_file_write(PG_FUNCTION_ARGS)
if (count != VARSIZE(data) - VARHDRSZ) if (count != VARSIZE(data) - VARHDRSZ)
ereport(ERROR, ereport(ERROR,
(errcode_for_file_access(), (errcode_for_file_access(),
errmsg("error writing file %s: %m", filename))); errmsg("could not write file \"%s\": %m", filename)));
} }
fclose(f); fclose(f);
@ -181,18 +184,18 @@ pg_file_rename(PG_FUNCTION_ARGS)
if (PG_ARGISNULL(0) || PG_ARGISNULL(1)) if (PG_ARGISNULL(0) || PG_ARGISNULL(1))
PG_RETURN_NULL(); PG_RETURN_NULL();
fn1 = absClusterPath(PG_GETARG_TEXT_P(0), false); fn1 = convert_and_check_filename(PG_GETARG_TEXT_P(0), false);
fn2 = absClusterPath(PG_GETARG_TEXT_P(1), false); fn2 = convert_and_check_filename(PG_GETARG_TEXT_P(1), false);
if (PG_ARGISNULL(2)) if (PG_ARGISNULL(2))
fn3 = 0; fn3 = 0;
else else
fn3 = absClusterPath(PG_GETARG_TEXT_P(2), false); fn3 = convert_and_check_filename(PG_GETARG_TEXT_P(2), false);
if (access(fn1, W_OK) < 0) if (access(fn1, W_OK) < 0)
{ {
ereport(WARNING, ereport(WARNING,
(errcode_for_file_access(), (errcode_for_file_access(),
errmsg("file %s not accessible: %m", fn1))); errmsg("file \"%s\" is not accessible: %m", fn1)));
PG_RETURN_BOOL(false); PG_RETURN_BOOL(false);
} }
@ -201,18 +204,18 @@ pg_file_rename(PG_FUNCTION_ARGS)
{ {
ereport(WARNING, ereport(WARNING,
(errcode_for_file_access(), (errcode_for_file_access(),
errmsg("file %s not accessible: %m", fn2))); errmsg("file \"%s\" is not accessible: %m", fn2)));
PG_RETURN_BOOL(false); PG_RETURN_BOOL(false);
} }
rc = access(fn3 ? fn3 : fn2, 2); rc = access(fn3 ? fn3 : fn2, 2);
if (rc >= 0 || errno != ENOENT) if (rc >= 0 || errno != ENOENT)
{ {
ereport(ERROR, ereport(ERROR,
(ERRCODE_DUPLICATE_FILE, (ERRCODE_DUPLICATE_FILE,
errmsg("cannot rename to target file %s", fn3 ? fn3 : fn2))); errmsg("cannot rename to target file \"%s\"",
fn3 ? fn3 : fn2)));
} }
if (fn3) if (fn3)
@ -221,37 +224,37 @@ pg_file_rename(PG_FUNCTION_ARGS)
{ {
ereport(ERROR, ereport(ERROR,
(errcode_for_file_access(), (errcode_for_file_access(),
errmsg("could not rename %s to %s: %m", fn2, fn3))); errmsg("could not rename \"%s\" to \"%s\": %m",
fn2, fn3)));
} }
if (rename(fn1, fn2) != 0) if (rename(fn1, fn2) != 0)
{ {
ereport(WARNING, ereport(WARNING,
(errcode_for_file_access(), (errcode_for_file_access(),
errmsg("could not rename %s to %s: %m", fn1, fn2))); errmsg("could not rename \"%s\" to \"%s\": %m",
fn1, fn2)));
if (rename(fn3, fn2) != 0) if (rename(fn3, fn2) != 0)
{ {
ereport(ERROR, ereport(ERROR,
(errcode_for_file_access(), (errcode_for_file_access(),
errmsg("could not rename %s back to %s: %m", fn3, fn2))); errmsg("could not rename \"%s\" back to \"%s\": %m",
fn3, fn2)));
} }
else else
{ {
ereport(ERROR, ereport(ERROR,
(ERRCODE_UNDEFINED_FILE, (ERRCODE_UNDEFINED_FILE,
errmsg("renaming %s to %s was reverted", fn2, fn3))); errmsg("renaming \"%s\" to \"%s\" was reverted",
fn2, fn3)));
} }
} }
} }
else if (rename(fn1, fn2) != 0) else if (rename(fn1, fn2) != 0)
{ {
ereport(WARNING,
(errcode_for_file_access(),
errmsg("renaming %s to %s %m", fn1, fn2)));
ereport(ERROR, ereport(ERROR,
(errcode_for_file_access(), (errcode_for_file_access(),
errmsg("could not rename %s to %s: %m", fn1, fn2))); errmsg("could not rename \"%s\" to \"%s\": %m", fn1, fn2)));
} }
PG_RETURN_BOOL(true); PG_RETURN_BOOL(true);
@ -265,7 +268,7 @@ pg_file_unlink(PG_FUNCTION_ARGS)
requireSuperuser(); requireSuperuser();
filename = absClusterPath(PG_GETARG_TEXT_P(0), false); filename = convert_and_check_filename(PG_GETARG_TEXT_P(0), false);
if (access(filename, W_OK) < 0) if (access(filename, W_OK) < 0)
{ {
@ -274,15 +277,14 @@ pg_file_unlink(PG_FUNCTION_ARGS)
else else
ereport(ERROR, ereport(ERROR,
(errcode_for_file_access(), (errcode_for_file_access(),
errmsg("file %s not accessible: %m", filename))); errmsg("file \"%s\" is not accessible: %m", filename)));
} }
if (unlink(filename) < 0) if (unlink(filename) < 0)
{ {
ereport(WARNING, ereport(WARNING,
(errcode_for_file_access(), (errcode_for_file_access(),
errmsg("could not unlink file %s: %m", filename))); errmsg("could not unlink file \"%s\": %m", filename)));
PG_RETURN_BOOL(false); PG_RETURN_BOOL(false);
} }
@ -316,13 +318,7 @@ pg_logdir_ls(PG_FUNCTION_ARGS)
oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
fctx = palloc(sizeof(directory_fctx)); fctx = palloc(sizeof(directory_fctx));
if (is_absolute_path(Log_directory))
fctx->location = pstrdup(Log_directory);
else
{
fctx->location = palloc(strlen(DataDir) + strlen(Log_directory) + 2);
sprintf(fctx->location, "%s/%s", DataDir, Log_directory);
}
tupdesc = CreateTemplateTupleDesc(2, false); tupdesc = CreateTemplateTupleDesc(2, false);
TupleDescInitEntry(tupdesc, (AttrNumber) 1, "starttime", TupleDescInitEntry(tupdesc, (AttrNumber) 1, "starttime",
TIMESTAMPOID, -1, 0); TIMESTAMPOID, -1, 0);
@ -331,12 +327,14 @@ pg_logdir_ls(PG_FUNCTION_ARGS)
funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc); funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc);
fctx->location = pstrdup(Log_directory);
fctx->dirdesc = AllocateDir(fctx->location); fctx->dirdesc = AllocateDir(fctx->location);
if (!fctx->dirdesc) if (!fctx->dirdesc)
ereport(ERROR, ereport(ERROR,
(errcode_for_file_access(), (errcode_for_file_access(),
errmsg("%s is not browsable: %m", fctx->location))); errmsg("could not read directory \"%s\": %m",
fctx->location)));
funcctx->user_fctx = fctx; funcctx->user_fctx = fctx;
MemoryContextSwitchTo(oldcontext); MemoryContextSwitchTo(oldcontext);

View File

@ -5,7 +5,7 @@
* Copyright (c) 2002-2006, PostgreSQL Global Development Group * Copyright (c) 2002-2006, PostgreSQL Global Development Group
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/adt/dbsize.c,v 1.8 2006/03/05 15:58:41 momjian Exp $ * $PostgreSQL: pgsql/src/backend/utils/adt/dbsize.c,v 1.9 2006/11/06 03:06:41 tgl Exp $
* *
*/ */
@ -15,6 +15,7 @@
#include <sys/stat.h> #include <sys/stat.h>
#include "access/heapam.h" #include "access/heapam.h"
#include "catalog/catalog.h"
#include "catalog/namespace.h" #include "catalog/namespace.h"
#include "catalog/pg_tablespace.h" #include "catalog/pg_tablespace.h"
#include "commands/dbcommands.h" #include "commands/dbcommands.h"
@ -77,11 +78,11 @@ calculate_database_size(Oid dbOid)
/* Shared storage in pg_global is not counted */ /* Shared storage in pg_global is not counted */
/* Include pg_default storage */ /* Include pg_default storage */
snprintf(pathname, MAXPGPATH, "%s/base/%u", DataDir, dbOid); snprintf(pathname, MAXPGPATH, "base/%u", dbOid);
totalsize = db_dir_size(pathname); totalsize = db_dir_size(pathname);
/* Scan the non-default tablespaces */ /* Scan the non-default tablespaces */
snprintf(dirpath, MAXPGPATH, "%s/pg_tblspc", DataDir); snprintf(dirpath, MAXPGPATH, "pg_tblspc");
dirdesc = AllocateDir(dirpath); dirdesc = AllocateDir(dirpath);
if (!dirdesc) if (!dirdesc)
ereport(ERROR, ereport(ERROR,
@ -95,8 +96,8 @@ calculate_database_size(Oid dbOid)
strcmp(direntry->d_name, "..") == 0) strcmp(direntry->d_name, "..") == 0)
continue; continue;
snprintf(pathname, MAXPGPATH, "%s/pg_tblspc/%s/%u", snprintf(pathname, MAXPGPATH, "pg_tblspc/%s/%u",
DataDir, direntry->d_name, dbOid); direntry->d_name, dbOid);
totalsize += db_dir_size(pathname); totalsize += db_dir_size(pathname);
} }
@ -148,11 +149,11 @@ calculate_tablespace_size(Oid tblspcOid)
struct dirent *direntry; struct dirent *direntry;
if (tblspcOid == DEFAULTTABLESPACE_OID) if (tblspcOid == DEFAULTTABLESPACE_OID)
snprintf(tblspcPath, MAXPGPATH, "%s/base", DataDir); snprintf(tblspcPath, MAXPGPATH, "base");
else if (tblspcOid == GLOBALTABLESPACE_OID) else if (tblspcOid == GLOBALTABLESPACE_OID)
snprintf(tblspcPath, MAXPGPATH, "%s/global", DataDir); snprintf(tblspcPath, MAXPGPATH, "global");
else else
snprintf(tblspcPath, MAXPGPATH, "%s/pg_tblspc/%u", DataDir, tblspcOid); snprintf(tblspcPath, MAXPGPATH, "pg_tblspc/%u", tblspcOid);
dirdesc = AllocateDir(tblspcPath); dirdesc = AllocateDir(tblspcPath);
@ -219,30 +220,22 @@ static int64
calculate_relation_size(RelFileNode *rfn) calculate_relation_size(RelFileNode *rfn)
{ {
int64 totalsize = 0; int64 totalsize = 0;
char dirpath[MAXPGPATH]; char *relationpath;
char pathname[MAXPGPATH]; char pathname[MAXPGPATH];
unsigned int segcount = 0; unsigned int segcount = 0;
Assert(OidIsValid(rfn->spcNode)); relationpath = relpath(*rfn);
if (rfn->spcNode == DEFAULTTABLESPACE_OID)
snprintf(dirpath, MAXPGPATH, "%s/base/%u", DataDir, rfn->dbNode);
else if (rfn->spcNode == GLOBALTABLESPACE_OID)
snprintf(dirpath, MAXPGPATH, "%s/global", DataDir);
else
snprintf(dirpath, MAXPGPATH, "%s/pg_tblspc/%u/%u",
DataDir, rfn->spcNode, rfn->dbNode);
for (segcount = 0;; segcount++) for (segcount = 0;; segcount++)
{ {
struct stat fst; struct stat fst;
if (segcount == 0) if (segcount == 0)
snprintf(pathname, MAXPGPATH, "%s/%u", snprintf(pathname, MAXPGPATH, "%s",
dirpath, rfn->relNode); relationpath);
else else
snprintf(pathname, MAXPGPATH, "%s/%u.%u", snprintf(pathname, MAXPGPATH, "%s.%u",
dirpath, rfn->relNode, segcount); relationpath, segcount);
if (stat(pathname, &fst) < 0) if (stat(pathname, &fst) < 0)
{ {
@ -296,8 +289,7 @@ pg_relation_size_name(PG_FUNCTION_ARGS)
/* /*
* Compute the on-disk size of files for the relation according to the * Compute the on-disk size of files for the relation according to the
* stat function, optionally including heap data, index data, and/or * stat function, including heap data, index data, and toast data.
* toast data.
*/ */
static int64 static int64
calculate_total_relation_size(Oid Relid) calculate_total_relation_size(Oid Relid)
@ -313,10 +305,9 @@ calculate_total_relation_size(Oid Relid)
/* Get the heap size */ /* Get the heap size */
size = calculate_relation_size(&(heapRel->rd_node)); size = calculate_relation_size(&(heapRel->rd_node));
/* Get index size */ /* Include any dependent indexes */
if (heapRel->rd_rel->relhasindex) if (heapRel->rd_rel->relhasindex)
{ {
/* recursively include any dependent indexes */
List *index_oids = RelationGetIndexList(heapRel); List *index_oids = RelationGetIndexList(heapRel);
foreach(cell, index_oids) foreach(cell, index_oids)
@ -334,7 +325,7 @@ calculate_total_relation_size(Oid Relid)
list_free(index_oids); list_free(index_oids);
} }
/* Get toast table (and index) size */ /* Recursively include toast table (and index) size */
if (OidIsValid(toastOid)) if (OidIsValid(toastOid))
size += calculate_total_relation_size(toastOid); size += calculate_total_relation_size(toastOid);
@ -343,10 +334,6 @@ calculate_total_relation_size(Oid Relid)
return size; return size;
} }
/*
* Compute on-disk size of files for 'relation' including
* heap data, index data, and toasted data.
*/
Datum Datum
pg_total_relation_size_oid(PG_FUNCTION_ARGS) pg_total_relation_size_oid(PG_FUNCTION_ARGS)
{ {

View File

@ -9,7 +9,7 @@
* Author: Andreas Pflug <pgadmin@pse-consulting.de> * Author: Andreas Pflug <pgadmin@pse-consulting.de>
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/adt/genfile.c,v 1.11 2006/07/13 16:49:16 momjian Exp $ * $PostgreSQL: pgsql/src/backend/utils/adt/genfile.c,v 1.12 2006/11/06 03:06:41 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -38,13 +38,13 @@ typedef struct
/* /*
* Validate a path and convert to absolute form. * Convert a "text" filename argument to C string, and check it's allowable.
* *
* Argument may be absolute or relative to the DataDir (but we only allow * Filename may be absolute or relative to the DataDir, but we only allow
* absolute paths that match DataDir or Log_directory). * absolute paths that match DataDir or Log_directory.
*/ */
static char * static char *
check_and_make_absolute(text *arg) convert_and_check_filename(text *arg)
{ {
int input_len = VARSIZE(arg) - VARHDRSZ; int input_len = VARSIZE(arg) - VARHDRSZ;
char *filename = palloc(input_len + 1); char *filename = palloc(input_len + 1);
@ -77,11 +77,7 @@ check_and_make_absolute(text *arg)
} }
else else
{ {
char *absname = palloc(strlen(DataDir) + strlen(filename) + 2); return filename;
sprintf(absname, "%s/%s", DataDir, filename);
pfree(filename);
return absname;
} }
} }
@ -105,7 +101,7 @@ pg_read_file(PG_FUNCTION_ARGS)
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
(errmsg("must be superuser to read files")))); (errmsg("must be superuser to read files"))));
filename = check_and_make_absolute(filename_t); filename = convert_and_check_filename(filename_t);
if ((file = AllocateFile(filename, PG_BINARY_R)) == NULL) if ((file = AllocateFile(filename, PG_BINARY_R)) == NULL)
ereport(ERROR, ereport(ERROR,
@ -166,7 +162,7 @@ pg_stat_file(PG_FUNCTION_ARGS)
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
(errmsg("must be superuser to get file information")))); (errmsg("must be superuser to get file information"))));
filename = check_and_make_absolute(filename_t); filename = convert_and_check_filename(filename_t);
if (stat(filename, &fst) < 0) if (stat(filename, &fst) < 0)
ereport(ERROR, ereport(ERROR,
@ -238,7 +234,7 @@ pg_ls_dir(PG_FUNCTION_ARGS)
oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
fctx = palloc(sizeof(directory_fctx)); fctx = palloc(sizeof(directory_fctx));
fctx->location = check_and_make_absolute(PG_GETARG_TEXT_P(0)); fctx->location = convert_and_check_filename(PG_GETARG_TEXT_P(0));
fctx->dirdesc = AllocateDir(fctx->location); fctx->dirdesc = AllocateDir(fctx->location);