88 *
99 *
1010 * IDENTIFICATION
11- * $Header: /cvsroot/pgsql/src/backend/commands/dbcommands.c,v 1.63 2000/10/28 16:20:54 vadim Exp $
11+ * $Header: /cvsroot/pgsql/src/backend/commands/dbcommands.c,v 1.64 2000/11/08 16:59:49 petere Exp $
1212 *
1313 *-------------------------------------------------------------------------
1414 */
1717
1818#include <errno.h>
1919#include <fcntl.h>
20- #include <stdlib.h>
21- #include <string.h>
2220#include <unistd.h>
2321#include <sys/stat.h>
2422#include <sys/types.h>
3735
3836
3937/* non-export function prototypes */
40- static bool
41- get_user_info (Oid use_sysid ,bool * use_super ,bool * use_createdb );
42-
43- static bool
44- get_db_info (const char * name ,char * dbpath ,Oid * dbIdP ,int4 * ownerIdP );
45-
38+ static bool get_user_info (Oid use_sysid ,bool * use_super ,bool * use_createdb );
39+ static bool get_db_info (const char * name ,char * dbpath ,Oid * dbIdP ,int4 * ownerIdP );
40+ static char * resolve_alt_dbpath (const char * dbpath ,Oid dboid );
41+ static bool remove_dbdirs (const char * real_loc ,const char * altloc );
4642
4743/*
4844 * CREATE DATABASE
5248createdb (const char * dbname ,const char * dbpath ,int encoding )
5349{
5450char buf [2 * MAXPGPATH + 100 ];
55- char * loc ;
56- char locbuf [ 512 ] ;
51+ char * altloc ;
52+ char * real_loc ;
5753int ret ;
5854bool use_super ,
5955use_createdb ;
@@ -77,24 +73,6 @@ createdb(const char *dbname, const char *dbpath, int encoding)
7773if (IsTransactionBlock ())
7874elog (ERROR ,"CREATE DATABASE: may not be called in a transaction block" );
7975
80- #ifdef OLD_FILE_NAMING
81- /* Generate directory name for the new database */
82- if (dbpath == NULL || strcmp (dbpath ,dbname )== 0 )
83- strcpy (locbuf ,dbname );
84- else
85- snprintf (locbuf ,sizeof (locbuf ),"%s/%s" ,dbpath ,dbname );
86-
87- loc = ExpandDatabasePath (locbuf );
88-
89- if (loc == NULL )
90- elog (ERROR ,
91- "The database path '%s' is invalid. "
92- "This may be due to a character that is not allowed or because the chosen "
93- "path isn't permitted for databases" ,dbpath );
94- #else
95- locbuf [0 ]= 0 ;/* Avoid junk in strings */
96- #endif
97-
9876/*
9977 * Insert a new tuple into pg_database
10078 */
@@ -108,13 +86,15 @@ createdb(const char *dbname, const char *dbpath, int encoding)
10886dboid = newoid ();
10987
11088/* Form tuple */
111- new_record [Anum_pg_database_datname - 1 ]= DirectFunctionCall1 ( namein ,
112- CStringGetDatum (dbname ));
89+ new_record [Anum_pg_database_datname - 1 ]=
90+ DirectFunctionCall1 ( namein , CStringGetDatum (dbname ));
11391new_record [Anum_pg_database_datdba - 1 ]= Int32GetDatum (GetUserId ());
11492new_record [Anum_pg_database_encoding - 1 ]= Int32GetDatum (encoding );
115- new_record [Anum_pg_database_datlastsysoid - 1 ]= ObjectIdGetDatum (dboid );/* Save current OID val */
116- new_record [Anum_pg_database_datpath - 1 ]= DirectFunctionCall1 (textin ,
117- CStringGetDatum (locbuf ));
93+ /* Save current OID val */
94+ new_record [Anum_pg_database_datlastsysoid - 1 ]= ObjectIdGetDatum (dboid );
95+ /* no nulls here, GetRawDatabaseInfo doesn't like them */
96+ new_record [Anum_pg_database_datpath - 1 ]=
97+ DirectFunctionCall1 (textin ,CStringGetDatum (dbpath ?dbpath :"" ));
11898
11999tuple = heap_formtuple (pg_database_dsc ,new_record ,new_record_nulls );
120100
@@ -126,9 +106,12 @@ createdb(const char *dbname, const char *dbpath, int encoding)
126106 */
127107heap_insert (pg_database_rel ,tuple );
128108
129- #ifndef OLD_FILE_NAMING
130- loc = GetDatabasePath (tuple -> t_data -> t_oid );
131- #endif
109+ real_loc = GetDatabasePath (tuple -> t_data -> t_oid );
110+ altloc = resolve_alt_dbpath (dbpath ,tuple -> t_data -> t_oid );
111+
112+ if (strchr (real_loc ,'\'' )&& strchr (altloc ,'\'' ))
113+ elog (ERROR ,"database path may not contain single quotes" );
114+ /* ... otherwise we'd be open to shell exploits below */
132115
133116/*
134117 * Update indexes (there aren't any currently)
@@ -156,41 +139,28 @@ createdb(const char *dbname, const char *dbpath, int encoding)
156139
157140/* Copy the template database to the new location */
158141
159- if (mkdir (loc ,S_IRWXU )!= 0 )
160- elog (ERROR ,"CREATE DATABASE: unable to create database directory '%s': %s" ,loc ,strerror (errno ));
142+ if (mkdir ((altloc ?altloc :real_loc ),S_IRWXU )!= 0 )
143+ elog (ERROR ,"CREATE DATABASE: unable to create database directory '%s': %s" ,
144+ (altloc ?altloc :real_loc ),strerror (errno ));
161145
162- #ifdef OLD_FILE_NAMING
163- snprintf (buf ,sizeof (buf ),"cp %s%cbase%ctemplate1%c* '%s'" ,
164- DataDir ,SEP_CHAR ,SEP_CHAR ,SEP_CHAR ,loc );
165- #else
146+ if (altloc )
166147{
167- char * tmpl = GetDatabasePath (TemplateDbOid );
168-
169- snprintf (buf ,sizeof (buf ),"cp %s%c* '%s'" ,
170- tmpl ,SEP_CHAR ,loc );
171- pfree (tmpl );
148+ if (symlink (altloc ,real_loc )!= 0 )
149+ elog (ERROR ,"CREATE DATABASE: could not link %s to %s: %s" ,
150+ real_loc ,altloc ,strerror (errno ));
172151}
173- #endif
152+
153+ snprintf (buf ,sizeof (buf ),"cp '%s'/* '%s'" ,
154+ GetDatabasePath (TemplateDbOid ),real_loc );
174155
175156ret = system (buf );
176157/* Some versions of SunOS seem to return ECHILD after a system() call */
177- #if defined(sun )
178158if (ret != 0 && errno != ECHILD )
179- #else
180- if (ret != 0 )
181- #endif
182159{
183- /* Failed, so try to clean up the created directory ... */
184- snprintf (buf ,sizeof (buf ),"rm -rf '%s'" ,loc );
185- ret = system (buf );
186- #if defined(sun )
187- if (ret == 0 || errno == ECHILD )
188- #else
189- if (ret == 0 )
190- #endif
160+ if (remove_dbdirs (real_loc ,altloc ))
191161elog (ERROR ,"CREATE DATABASE: could not initialize database directory" );
192162else
193- elog (ERROR ,"CREATE DATABASE:Could not initialize database directory. Delete failed as well" );
163+ elog (ERROR ,"CREATE DATABASE:could not initialize database directory; delete failed as well" );
194164}
195165
196166#ifdef XLOG
@@ -210,9 +180,9 @@ dropdb(const char *dbname)
210180int4 db_owner ;
211181bool use_super ;
212182Oid db_id ;
213- char * path ,
214- dbpath [ MAXPGPATH ],
215- buf [ MAXPGPATH + 100 ];
183+ char * altloc ;
184+ char * real_loc ;
185+ char dbpath [ MAXPGPATH ];
216186Relation pgdbrel ;
217187HeapScanDesc pgdbscan ;
218188ScanKeyData key ;
@@ -221,33 +191,25 @@ dropdb(const char *dbname)
221191AssertArg (dbname );
222192
223193if (strcmp (dbname ,"template1" )== 0 )
224- elog (ERROR ,"DROP DATABASE:May not be executed on the template1 database" );
194+ elog (ERROR ,"DROP DATABASE:may not be executed on the template1 database" );
225195
226196if (strcmp (dbname ,DatabaseName )== 0 )
227- elog (ERROR ,"DROP DATABASE:Cannot be executed on the currently open database" );
197+ elog (ERROR ,"DROP DATABASE:cannot be executed on the currently open database" );
228198
229199if (IsTransactionBlock ())
230- elog (ERROR ,"DROP DATABASE:May not be called in a transaction block" );
200+ elog (ERROR ,"DROP DATABASE:may not be called in a transaction block" );
231201
232202if (!get_user_info (GetUserId (),& use_super ,NULL ))
233- elog (ERROR ,"Current user name is invalid" );
203+ elog (ERROR ,"current user name is invalid" );
234204
235205if (!get_db_info (dbname ,dbpath ,& db_id ,& db_owner ))
236- elog (ERROR ,"DROP DATABASE:Database \"%s\" does not exist" ,dbname );
206+ elog (ERROR ,"DROP DATABASE:database \"%s\" does not exist" ,dbname );
237207
238208if (GetUserId ()!= db_owner && !use_super )
239- elog (ERROR ,"DROP DATABASE: Permission denied" );
240-
241- #ifdef OLD_FILE_NAMING
242- path = ExpandDatabasePath (dbpath );
243- if (path == NULL )
244- elog (ERROR ,
245- "The database path '%s' is invalid. "
246- "This may be due to a character that is not allowed or because the chosen "
247- "path isn't permitted for databases" ,path );
248- #else
249- path = GetDatabasePath (db_id );
250- #endif
209+ elog (ERROR ,"DROP DATABASE: permission denied" );
210+
211+ real_loc = GetDatabasePath (db_id );
212+ altloc = resolve_alt_dbpath (dbpath ,db_id );
251213
252214/*
253215 * Obtain exclusive lock on pg_database. We need this to ensure that
@@ -266,7 +228,7 @@ dropdb(const char *dbname)
266228if (DatabaseHasActiveBackends (db_id ))
267229{
268230heap_close (pgdbrel ,AccessExclusiveLock );
269- elog (ERROR ,"DROP DATABASE:Database \"%s\" is being accessed by other users" ,dbname );
231+ elog (ERROR ,"DROP DATABASE:database \"%s\" is being accessed by other users" ,dbname );
270232}
271233
272234/*
@@ -320,13 +282,7 @@ dropdb(const char *dbname)
320282/*
321283 * Remove the database's subdirectory and everything in it.
322284 */
323- snprintf (buf ,sizeof (buf ),"rm -rf '%s'" ,path );
324- #if defined(sun )
325- if (system (buf )!= 0 && errno != ECHILD )
326- #else
327- if (system (buf )!= 0 )
328- #endif
329- elog (NOTICE ,"DROP DATABASE: The database directory '%s' could not be removed" ,path );
285+ remove_dbdirs (real_loc ,altloc );
330286}
331287
332288
@@ -426,3 +382,66 @@ get_user_info(Oid use_sysid, bool *use_super, bool *use_createdb)
426382
427383return true;
428384}
385+
386+
387+ static char *
388+ resolve_alt_dbpath (const char * dbpath ,Oid dboid )
389+ {
390+ char * prefix ;
391+ char * ret ;
392+ size_t len ;
393+
394+ if (dbpath == NULL || dbpath [0 ]== '\0' )
395+ return NULL ;
396+
397+ if (strchr (dbpath ,'/' ))
398+ {
399+ #ifdef ALLOW_ABSOLUTE_DBPATHS
400+ prefix = dbpath ;
401+ #else
402+ elog (ERROR ,"Absolute paths are not allowed as database locations" );
403+ #endif
404+ }
405+ else
406+ {
407+ /* must be environment variable */
408+ char * var = getenv (dbpath );
409+ if (!var )
410+ elog (ERROR ,"environment variable %s not set" ,dbpath );
411+ if (var [0 ]!= '/' )
412+ elog (ERROR ,"environment variable %s must be absolute path" ,dbpath );
413+ prefix = var ;
414+ }
415+
416+ len = strlen (prefix )+ 6 + sizeof (Oid )* 8 + 1 ;
417+ ret = palloc (len );
418+ snprintf (ret ,len ,"%s/base/%u" ,prefix ,dboid );
419+
420+ return ret ;
421+ }
422+
423+
424+ static bool
425+ remove_dbdirs (const char * real_loc ,const char * altloc )
426+ {
427+ char buf [MAXPGPATH + 100 ];
428+ bool success = true;
429+
430+ if (altloc )
431+ /* remove symlink */
432+ if (unlink (real_loc )!= 0 )
433+ {
434+ elog (NOTICE ,"could not remove '%s': %s" ,real_loc ,strerror (errno ));
435+ success = false;
436+ }
437+
438+ snprintf (buf ,sizeof (buf ),"rm -rf '%s'" ,altloc ?altloc :real_loc );
439+ if (system (buf )!= 0 && errno != ECHILD )
440+ {
441+ elog (NOTICE ,"database directory '%s' could not be removed" ,
442+ altloc ?altloc :real_loc );
443+ success = false;
444+ }
445+
446+ return success ;
447+ }