00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015 #include "postgres.h"
00016
00017 #include <sys/file.h>
00018 #include <sys/stat.h>
00019 #include <unistd.h>
00020
00021 #include "catalog/pg_type.h"
00022 #include "funcapi.h"
00023 #include "miscadmin.h"
00024 #include "postmaster/syslogger.h"
00025 #include "storage/fd.h"
00026 #include "utils/builtins.h"
00027 #include "utils/datetime.h"
00028
00029
00030 #ifdef WIN32
00031
00032 #ifdef rename
00033 #undef rename
00034 #endif
00035
00036 #ifdef unlink
00037 #undef unlink
00038 #endif
00039 #endif
00040
00041 PG_MODULE_MAGIC;
00042
00043 Datum pg_file_write(PG_FUNCTION_ARGS);
00044 Datum pg_file_rename(PG_FUNCTION_ARGS);
00045 Datum pg_file_unlink(PG_FUNCTION_ARGS);
00046 Datum pg_logdir_ls(PG_FUNCTION_ARGS);
00047
00048 PG_FUNCTION_INFO_V1(pg_file_write);
00049 PG_FUNCTION_INFO_V1(pg_file_rename);
00050 PG_FUNCTION_INFO_V1(pg_file_unlink);
00051 PG_FUNCTION_INFO_V1(pg_logdir_ls);
00052
00053 typedef struct
00054 {
00055 char *location;
00056 DIR *dirdesc;
00057 } directory_fctx;
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069 static char *
00070 convert_and_check_filename(text *arg, bool logAllowed)
00071 {
00072 char *filename = text_to_cstring(arg);
00073
00074 canonicalize_path(filename);
00075
00076 if (is_absolute_path(filename))
00077 {
00078
00079 if (path_contains_parent_reference(filename))
00080 ereport(ERROR,
00081 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
00082 (errmsg("reference to parent directory (\"..\") not allowed"))));
00083
00084
00085
00086
00087
00088 if (!path_is_prefix_of_path(DataDir, filename) &&
00089 (!logAllowed || !is_absolute_path(Log_directory) ||
00090 !path_is_prefix_of_path(Log_directory, filename)))
00091 ereport(ERROR,
00092 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
00093 (errmsg("absolute path not allowed"))));
00094 }
00095 else if (!path_is_relative_and_below_cwd(filename))
00096 ereport(ERROR,
00097 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
00098 (errmsg("path must be in or below the current directory"))));
00099
00100 return filename;
00101 }
00102
00103
00104
00105
00106
00107 static void
00108 requireSuperuser(void)
00109 {
00110 if (!superuser())
00111 ereport(ERROR,
00112 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
00113 (errmsg("only superuser may access generic file functions"))));
00114 }
00115
00116
00117
00118
00119
00120
00121
00122 Datum
00123 pg_file_write(PG_FUNCTION_ARGS)
00124 {
00125 FILE *f;
00126 char *filename;
00127 text *data;
00128 int64 count = 0;
00129
00130 requireSuperuser();
00131
00132 filename = convert_and_check_filename(PG_GETARG_TEXT_P(0), false);
00133 data = PG_GETARG_TEXT_P(1);
00134
00135 if (!PG_GETARG_BOOL(2))
00136 {
00137 struct stat fst;
00138
00139 if (stat(filename, &fst) >= 0)
00140 ereport(ERROR,
00141 (ERRCODE_DUPLICATE_FILE,
00142 errmsg("file \"%s\" exists", filename)));
00143
00144 f = fopen(filename, "wb");
00145 }
00146 else
00147 f = fopen(filename, "ab");
00148
00149 if (!f)
00150 ereport(ERROR,
00151 (errcode_for_file_access(),
00152 errmsg("could not open file \"%s\" for writing: %m",
00153 filename)));
00154
00155 if (VARSIZE(data) != 0)
00156 {
00157 count = fwrite(VARDATA(data), 1, VARSIZE(data) - VARHDRSZ, f);
00158
00159 if (count != VARSIZE(data) - VARHDRSZ)
00160 ereport(ERROR,
00161 (errcode_for_file_access(),
00162 errmsg("could not write file \"%s\": %m", filename)));
00163 }
00164 fclose(f);
00165
00166 PG_RETURN_INT64(count);
00167 }
00168
00169
00170 Datum
00171 pg_file_rename(PG_FUNCTION_ARGS)
00172 {
00173 char *fn1,
00174 *fn2,
00175 *fn3;
00176 int rc;
00177
00178 requireSuperuser();
00179
00180 if (PG_ARGISNULL(0) || PG_ARGISNULL(1))
00181 PG_RETURN_NULL();
00182
00183 fn1 = convert_and_check_filename(PG_GETARG_TEXT_P(0), false);
00184 fn2 = convert_and_check_filename(PG_GETARG_TEXT_P(1), false);
00185 if (PG_ARGISNULL(2))
00186 fn3 = 0;
00187 else
00188 fn3 = convert_and_check_filename(PG_GETARG_TEXT_P(2), false);
00189
00190 if (access(fn1, W_OK) < 0)
00191 {
00192 ereport(WARNING,
00193 (errcode_for_file_access(),
00194 errmsg("file \"%s\" is not accessible: %m", fn1)));
00195
00196 PG_RETURN_BOOL(false);
00197 }
00198
00199 if (fn3 && access(fn2, W_OK) < 0)
00200 {
00201 ereport(WARNING,
00202 (errcode_for_file_access(),
00203 errmsg("file \"%s\" is not accessible: %m", fn2)));
00204
00205 PG_RETURN_BOOL(false);
00206 }
00207
00208 rc = access(fn3 ? fn3 : fn2, 2);
00209 if (rc >= 0 || errno != ENOENT)
00210 {
00211 ereport(ERROR,
00212 (ERRCODE_DUPLICATE_FILE,
00213 errmsg("cannot rename to target file \"%s\"",
00214 fn3 ? fn3 : fn2)));
00215 }
00216
00217 if (fn3)
00218 {
00219 if (rename(fn2, fn3) != 0)
00220 {
00221 ereport(ERROR,
00222 (errcode_for_file_access(),
00223 errmsg("could not rename \"%s\" to \"%s\": %m",
00224 fn2, fn3)));
00225 }
00226 if (rename(fn1, fn2) != 0)
00227 {
00228 ereport(WARNING,
00229 (errcode_for_file_access(),
00230 errmsg("could not rename \"%s\" to \"%s\": %m",
00231 fn1, fn2)));
00232
00233 if (rename(fn3, fn2) != 0)
00234 {
00235 ereport(ERROR,
00236 (errcode_for_file_access(),
00237 errmsg("could not rename \"%s\" back to \"%s\": %m",
00238 fn3, fn2)));
00239 }
00240 else
00241 {
00242 ereport(ERROR,
00243 (ERRCODE_UNDEFINED_FILE,
00244 errmsg("renaming \"%s\" to \"%s\" was reverted",
00245 fn2, fn3)));
00246 }
00247 }
00248 }
00249 else if (rename(fn1, fn2) != 0)
00250 {
00251 ereport(ERROR,
00252 (errcode_for_file_access(),
00253 errmsg("could not rename \"%s\" to \"%s\": %m", fn1, fn2)));
00254 }
00255
00256 PG_RETURN_BOOL(true);
00257 }
00258
00259
00260 Datum
00261 pg_file_unlink(PG_FUNCTION_ARGS)
00262 {
00263 char *filename;
00264
00265 requireSuperuser();
00266
00267 filename = convert_and_check_filename(PG_GETARG_TEXT_P(0), false);
00268
00269 if (access(filename, W_OK) < 0)
00270 {
00271 if (errno == ENOENT)
00272 PG_RETURN_BOOL(false);
00273 else
00274 ereport(ERROR,
00275 (errcode_for_file_access(),
00276 errmsg("file \"%s\" is not accessible: %m", filename)));
00277 }
00278
00279 if (unlink(filename) < 0)
00280 {
00281 ereport(WARNING,
00282 (errcode_for_file_access(),
00283 errmsg("could not unlink file \"%s\": %m", filename)));
00284
00285 PG_RETURN_BOOL(false);
00286 }
00287 PG_RETURN_BOOL(true);
00288 }
00289
00290
00291 Datum
00292 pg_logdir_ls(PG_FUNCTION_ARGS)
00293 {
00294 FuncCallContext *funcctx;
00295 struct dirent *de;
00296 directory_fctx *fctx;
00297
00298 if (!superuser())
00299 ereport(ERROR,
00300 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
00301 (errmsg("only superuser can list the log directory"))));
00302
00303 if (strcmp(Log_filename, "postgresql-%Y-%m-%d_%H%M%S.log") != 0)
00304 ereport(ERROR,
00305 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
00306 (errmsg("the log_filename parameter must equal 'postgresql-%%Y-%%m-%%d_%%H%%M%%S.log'"))));
00307
00308 if (SRF_IS_FIRSTCALL())
00309 {
00310 MemoryContext oldcontext;
00311 TupleDesc tupdesc;
00312
00313 funcctx = SRF_FIRSTCALL_INIT();
00314 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
00315
00316 fctx = palloc(sizeof(directory_fctx));
00317
00318 tupdesc = CreateTemplateTupleDesc(2, false);
00319 TupleDescInitEntry(tupdesc, (AttrNumber) 1, "starttime",
00320 TIMESTAMPOID, -1, 0);
00321 TupleDescInitEntry(tupdesc, (AttrNumber) 2, "filename",
00322 TEXTOID, -1, 0);
00323
00324 funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc);
00325
00326 fctx->location = pstrdup(Log_directory);
00327 fctx->dirdesc = AllocateDir(fctx->location);
00328
00329 if (!fctx->dirdesc)
00330 ereport(ERROR,
00331 (errcode_for_file_access(),
00332 errmsg("could not read directory \"%s\": %m",
00333 fctx->location)));
00334
00335 funcctx->user_fctx = fctx;
00336 MemoryContextSwitchTo(oldcontext);
00337 }
00338
00339 funcctx = SRF_PERCALL_SETUP();
00340 fctx = (directory_fctx *) funcctx->user_fctx;
00341
00342 while ((de = ReadDir(fctx->dirdesc, fctx->location)) != NULL)
00343 {
00344 char *values[2];
00345 HeapTuple tuple;
00346 char timestampbuf[32];
00347 char *field[MAXDATEFIELDS];
00348 char lowstr[MAXDATELEN + 1];
00349 int dtype;
00350 int nf,
00351 ftype[MAXDATEFIELDS];
00352 fsec_t fsec;
00353 int tz = 0;
00354 struct pg_tm date;
00355
00356
00357
00358
00359 if (strlen(de->d_name) != 32
00360 || strncmp(de->d_name, "postgresql-", 11) != 0
00361 || de->d_name[21] != '_'
00362 || strcmp(de->d_name + 28, ".log") != 0)
00363 continue;
00364
00365
00366 strcpy(timestampbuf, de->d_name + 11);
00367 timestampbuf[17] = '\0';
00368
00369
00370 if (ParseDateTime(timestampbuf, lowstr, MAXDATELEN, field, ftype, MAXDATEFIELDS, &nf))
00371 continue;
00372
00373 if (DecodeDateTime(field, ftype, nf, &dtype, &date, &fsec, &tz))
00374 continue;
00375
00376
00377
00378 values[0] = timestampbuf;
00379 values[1] = palloc(strlen(fctx->location) + strlen(de->d_name) + 2);
00380 sprintf(values[1], "%s/%s", fctx->location, de->d_name);
00381
00382 tuple = BuildTupleFromCStrings(funcctx->attinmeta, values);
00383
00384 SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuple));
00385 }
00386
00387 FreeDir(fctx->dirdesc);
00388 SRF_RETURN_DONE(funcctx);
00389 }