YugabyteDB (2.13.0.0-b42, bfc6a6643e7399ac8a0e81d06a3ee6d6571b33ab)

Coverage Report

Created: 2022-03-09 17:30

/Users/deen/code/yugabyte-db/src/postgres/src/backend/utils/adt/genfile.c
Line
Count
Source (jump to first uncovered line)
1
/*-------------------------------------------------------------------------
2
 *
3
 * genfile.c
4
 *    Functions for direct access to files
5
 *
6
 *
7
 * Copyright (c) 2004-2018, PostgreSQL Global Development Group
8
 *
9
 * Author: Andreas Pflug <pgadmin@pse-consulting.de>
10
 *
11
 * IDENTIFICATION
12
 *    src/backend/utils/adt/genfile.c
13
 *
14
 *-------------------------------------------------------------------------
15
 */
16
#include "postgres.h"
17
18
#include <sys/file.h>
19
#include <sys/stat.h>
20
#include <unistd.h>
21
#include <dirent.h>
22
23
#include "access/htup_details.h"
24
#include "access/xlog_internal.h"
25
#include "catalog/pg_authid.h"
26
#include "catalog/pg_type.h"
27
#include "funcapi.h"
28
#include "mb/pg_wchar.h"
29
#include "miscadmin.h"
30
#include "postmaster/syslogger.h"
31
#include "storage/fd.h"
32
#include "utils/builtins.h"
33
#include "utils/memutils.h"
34
#include "utils/timestamp.h"
35
36
typedef struct
37
{
38
  char     *location;
39
  DIR      *dirdesc;
40
  bool    include_dot_dirs;
41
} directory_fctx;
42
43
44
/*
45
 * Convert a "text" filename argument to C string, and check it's allowable.
46
 *
47
 * Filename may be absolute or relative to the DataDir, but we only allow
48
 * absolute paths that match DataDir or Log_directory.
49
 *
50
 * This does a privilege check against the 'pg_read_server_files' role, so
51
 * this function is really only appropriate for callers who are only checking
52
 * 'read' access.  Do not use this function if you are looking for a check
53
 * for 'write' or 'program' access without updating it to access the type
54
 * of check as an argument and checking the appropriate role membership.
55
 */
56
static char *
57
convert_and_check_filename(text *arg)
58
18
{
59
18
  char     *filename;
60
61
18
  filename = text_to_cstring(arg);
62
18
  canonicalize_path(filename);  /* filename can change length here */
63
64
  /*
65
   * Members of the 'pg_read_server_files' role are allowed to access any
66
   * files on the server as the PG user, so no need to do any further checks
67
   * here.
68
   */
69
18
  if (is_member_of_role(GetUserId(), DEFAULT_ROLE_READ_SERVER_FILES))
70
10
    return filename;
71
72
  /* User isn't a member of the default role, so check if it's allowable */
73
8
  if (is_absolute_path(filename))
74
8
  {
75
    /* Disallow '/a/b/data/..' */
76
8
    if (path_contains_parent_reference(filename))
77
8
      ereport(ERROR,
78
8
          (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
79
8
           (errmsg("reference to parent directory (\"..\") not allowed"))));
80
81
    /*
82
     * Allow absolute paths if within DataDir or Log_directory, even
83
     * though Log_directory might be outside DataDir.
84
     */
85
8
    if (!path_is_prefix_of_path(DataDir, filename) &&
86
8
      (!is_absolute_path(Log_directory) ||
87
0
       !path_is_prefix_of_path(Log_directory, filename)))
88
8
      ereport(ERROR,
89
8
          (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
90
8
           (errmsg("absolute path not allowed"))));
91
8
  }
92
0
  else if (!path_is_relative_and_below_cwd(filename))
93
0
    ereport(ERROR,
94
8
        (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
95
8
         (errmsg("path must be in or below the current directory"))));
96
97
8
  return filename;
98
8
}
99
100
101
/*
102
 * Read a section of a file, returning it as bytea
103
 *
104
 * Caller is responsible for all permissions checking.
105
 *
106
 * We read the whole of the file when bytes_to_read is negative.
107
 */
108
static bytea *
109
read_binary_file(const char *filename, int64 seek_offset, int64 bytes_to_read,
110
         bool missing_ok)
111
10
{
112
10
  bytea    *buf;
113
10
  size_t    nbytes;
114
10
  FILE     *file;
115
116
10
  if (bytes_to_read < 0)
117
8
  {
118
8
    if (seek_offset < 0)
119
0
      bytes_to_read = -seek_offset;
120
8
    else
121
8
    {
122
8
      struct stat fst;
123
124
8
      if (stat(filename, &fst) < 0)
125
2
      {
126
2
        if (missing_ok && errno == ENOENT)
127
0
          return NULL;
128
2
        else
129
2
          ereport(ERROR,
130
2
              (errcode_for_file_access(),
131
2
               errmsg("could not stat file \"%s\": %m", filename)));
132
2
      }
133
134
8
      bytes_to_read = fst.st_size - seek_offset;
135
8
    }
136
8
  }
137
138
  /* not sure why anyone thought that int64 length was a good idea */
139
10
  if (bytes_to_read > (MaxAllocSize - VARHDRSZ))
140
10
    ereport(ERROR,
141
10
        (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
142
10
         errmsg("requested length too large")));
143
144
10
  if ((file = AllocateFile(filename, PG_BINARY_R)) == NULL)
145
2
  {
146
2
    if (missing_ok && errno == ENOENT)
147
2
      return NULL;
148
2
    else
149
2
      ereport(ERROR,
150
2
          (errcode_for_file_access(),
151
2
           errmsg("could not open file \"%s\" for reading: %m",
152
2
              filename)));
153
2
  }
154
155
8
  if (fseeko(file, (off_t) seek_offset,
156
8
         (seek_offset >= 0) ? SEEK_SET : SEEK_END) != 0)
157
8
    ereport(ERROR,
158
8
        (errcode_for_file_access(),
159
8
         errmsg("could not seek in file \"%s\": %m", filename)));
160
161
8
  buf = (bytea *) palloc((Size) bytes_to_read + VARHDRSZ);
162
163
8
  nbytes = fread(VARDATA(buf), 1, (size_t) bytes_to_read, file);
164
165
8
  if (ferror(file))
166
8
    ereport(ERROR,
167
8
        (errcode_for_file_access(),
168
8
         errmsg("could not read file \"%s\": %m", filename)));
169
170
8
  SET_VARSIZE(buf, nbytes + VARHDRSZ);
171
172
8
  FreeFile(file);
173
174
8
  return buf;
175
8
}
176
177
/*
178
 * Similar to read_binary_file, but we verify that the contents are valid
179
 * in the database encoding.
180
 */
181
static text *
182
read_text_file(const char *filename, int64 seek_offset, int64 bytes_to_read,
183
         bool missing_ok)
184
10
{
185
10
  bytea    *buf;
186
187
10
  buf = read_binary_file(filename, seek_offset, bytes_to_read, missing_ok);
188
189
10
  if (buf != NULL)
190
6
  {
191
    /* Make sure the input is valid */
192
6
    pg_verifymbstr(VARDATA(buf), VARSIZE(buf) - VARHDRSZ, false);
193
194
    /* OK, we can cast it to text safely */
195
6
    return (text *) buf;
196
6
  }
197
4
  else
198
4
    return NULL;
199
10
}
200
201
/*
202
 * Read a section of a file, returning it as text
203
 *
204
 * This function is kept to support adminpack 1.0.
205
 */
206
Datum
207
pg_read_file(PG_FUNCTION_ARGS)
208
0
{
209
0
  text     *filename_t = PG_GETARG_TEXT_PP(0);
210
0
  int64   seek_offset = 0;
211
0
  int64   bytes_to_read = -1;
212
0
  bool    missing_ok = false;
213
0
  char     *filename;
214
0
  text     *result;
215
216
0
  if (!superuser())
217
0
    ereport(ERROR,
218
0
        (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
219
0
         (errmsg("must be superuser to read files with adminpack 1.0"),
220
0
          errhint("Consider using pg_file_read(), which is part of core, instead."))));
221
222
  /* handle optional arguments */
223
0
  if (PG_NARGS() >= 3)
224
0
  {
225
0
    seek_offset = PG_GETARG_INT64(1);
226
0
    bytes_to_read = PG_GETARG_INT64(2);
227
228
0
    if (bytes_to_read < 0)
229
0
      ereport(ERROR,
230
0
          (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
231
0
           errmsg("requested length cannot be negative")));
232
0
  }
233
0
  if (PG_NARGS() >= 4)
234
0
    missing_ok = PG_GETARG_BOOL(3);
235
236
0
  filename = convert_and_check_filename(filename_t);
237
238
0
  result = read_text_file(filename, seek_offset, bytes_to_read, missing_ok);
239
0
  if (result)
240
0
    PG_RETURN_TEXT_P(result);
241
0
  else
242
0
    PG_RETURN_NULL();
243
0
}
244
245
/*
246
 * Read a section of a file, returning it as text
247
 *
248
 * No superuser check done here- instead privileges are handled by the
249
 * GRANT system.
250
 */
251
Datum
252
pg_read_file_v2(PG_FUNCTION_ARGS)
253
18
{
254
18
  text     *filename_t = PG_GETARG_TEXT_PP(0);
255
18
  int64   seek_offset = 0;
256
18
  int64   bytes_to_read = -1;
257
18
  bool    missing_ok = false;
258
18
  char     *filename;
259
18
  text     *result;
260
261
  /* handle optional arguments */
262
18
  if (PG_NARGS() >= 3)
263
10
  {
264
10
    seek_offset = PG_GETARG_INT64(1);
265
10
    bytes_to_read = PG_GETARG_INT64(2);
266
267
10
    if (bytes_to_read < 0)
268
10
      ereport(ERROR,
269
10
          (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
270
10
           errmsg("requested length cannot be negative")));
271
10
  }
272
18
  if (PG_NARGS() >= 4)
273
10
    missing_ok = PG_GETARG_BOOL(3);
274
275
18
  filename = convert_and_check_filename(filename_t);
276
277
18
  result = read_text_file(filename, seek_offset, bytes_to_read, missing_ok);
278
18
  if (result)
279
18
    PG_RETURN_TEXT_P(result);
280
18
  else
281
12
    PG_RETURN_NULL();
282
18
}
283
284
/*
285
 * Read a section of a file, returning it as bytea
286
 */
287
Datum
288
pg_read_binary_file(PG_FUNCTION_ARGS)
289
0
{
290
0
  text     *filename_t = PG_GETARG_TEXT_PP(0);
291
0
  int64   seek_offset = 0;
292
0
  int64   bytes_to_read = -1;
293
0
  bool    missing_ok = false;
294
0
  char     *filename;
295
0
  bytea    *result;
296
297
  /* handle optional arguments */
298
0
  if (PG_NARGS() >= 3)
299
0
  {
300
0
    seek_offset = PG_GETARG_INT64(1);
301
0
    bytes_to_read = PG_GETARG_INT64(2);
302
303
0
    if (bytes_to_read < 0)
304
0
      ereport(ERROR,
305
0
          (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
306
0
           errmsg("requested length cannot be negative")));
307
0
  }
308
0
  if (PG_NARGS() >= 4)
309
0
    missing_ok = PG_GETARG_BOOL(3);
310
311
0
  filename = convert_and_check_filename(filename_t);
312
313
0
  result = read_binary_file(filename, seek_offset,
314
0
                bytes_to_read, missing_ok);
315
0
  if (result)
316
0
    PG_RETURN_BYTEA_P(result);
317
0
  else
318
0
    PG_RETURN_NULL();
319
0
}
320
321
322
/*
323
 * Wrapper functions for the 1 and 3 argument variants of pg_read_file_v2()
324
 * and pg_binary_read_file().
325
 *
326
 * These are necessary to pass the sanity check in opr_sanity, which checks
327
 * that all built-in functions that share the implementing C function take
328
 * the same number of arguments.
329
 */
330
Datum
331
pg_read_file_off_len(PG_FUNCTION_ARGS)
332
0
{
333
0
  return pg_read_file_v2(fcinfo);
334
0
}
335
336
Datum
337
pg_read_file_all(PG_FUNCTION_ARGS)
338
8
{
339
8
  return pg_read_file_v2(fcinfo);
340
8
}
341
342
Datum
343
pg_read_binary_file_off_len(PG_FUNCTION_ARGS)
344
0
{
345
0
  return pg_read_binary_file(fcinfo);
346
0
}
347
348
Datum
349
pg_read_binary_file_all(PG_FUNCTION_ARGS)
350
0
{
351
0
  return pg_read_binary_file(fcinfo);
352
0
}
353
354
/*
355
 * stat a file
356
 */
357
Datum
358
pg_stat_file(PG_FUNCTION_ARGS)
359
0
{
360
0
  text     *filename_t = PG_GETARG_TEXT_PP(0);
361
0
  char     *filename;
362
0
  struct stat fst;
363
0
  Datum   values[6];
364
0
  bool    isnull[6];
365
0
  HeapTuple tuple;
366
0
  TupleDesc tupdesc;
367
0
  bool    missing_ok = false;
368
369
  /* check the optional argument */
370
0
  if (PG_NARGS() == 2)
371
0
    missing_ok = PG_GETARG_BOOL(1);
372
373
0
  filename = convert_and_check_filename(filename_t);
374
375
0
  if (stat(filename, &fst) < 0)
376
0
  {
377
0
    if (missing_ok && errno == ENOENT)
378
0
      PG_RETURN_NULL();
379
0
    else
380
0
      ereport(ERROR,
381
0
          (errcode_for_file_access(),
382
0
           errmsg("could not stat file \"%s\": %m", filename)));
383
0
  }
384
385
  /*
386
   * This record type had better match the output parameters declared for me
387
   * in pg_proc.h.
388
   */
389
0
  tupdesc = CreateTemplateTupleDesc(6, false);
390
0
  TupleDescInitEntry(tupdesc, (AttrNumber) 1,
391
0
             "size", INT8OID, -1, 0);
392
0
  TupleDescInitEntry(tupdesc, (AttrNumber) 2,
393
0
             "access", TIMESTAMPTZOID, -1, 0);
394
0
  TupleDescInitEntry(tupdesc, (AttrNumber) 3,
395
0
             "modification", TIMESTAMPTZOID, -1, 0);
396
0
  TupleDescInitEntry(tupdesc, (AttrNumber) 4,
397
0
             "change", TIMESTAMPTZOID, -1, 0);
398
0
  TupleDescInitEntry(tupdesc, (AttrNumber) 5,
399
0
             "creation", TIMESTAMPTZOID, -1, 0);
400
0
  TupleDescInitEntry(tupdesc, (AttrNumber) 6,
401
0
             "isdir", BOOLOID, -1, 0);
402
0
  BlessTupleDesc(tupdesc);
403
404
0
  memset(isnull, false, sizeof(isnull));
405
406
0
  values[0] = Int64GetDatum((int64) fst.st_size);
407
0
  values[1] = TimestampTzGetDatum(time_t_to_timestamptz(fst.st_atime));
408
0
  values[2] = TimestampTzGetDatum(time_t_to_timestamptz(fst.st_mtime));
409
  /* Unix has file status change time, while Win32 has creation time */
410
0
#if !defined(WIN32) && !defined(__CYGWIN__)
411
0
  values[3] = TimestampTzGetDatum(time_t_to_timestamptz(fst.st_ctime));
412
0
  isnull[4] = true;
413
#else
414
  isnull[3] = true;
415
  values[4] = TimestampTzGetDatum(time_t_to_timestamptz(fst.st_ctime));
416
#endif
417
0
  values[5] = BoolGetDatum(S_ISDIR(fst.st_mode));
418
419
0
  tuple = heap_form_tuple(tupdesc, values, isnull);
420
421
0
  pfree(filename);
422
423
0
  PG_RETURN_DATUM(HeapTupleGetDatum(tuple));
424
0
}
425
426
/*
427
 * stat a file (1 argument version)
428
 *
429
 * note: this wrapper is necessary to pass the sanity check in opr_sanity,
430
 * which checks that all built-in functions that share the implementing C
431
 * function take the same number of arguments
432
 */
433
Datum
434
pg_stat_file_1arg(PG_FUNCTION_ARGS)
435
0
{
436
0
  return pg_stat_file(fcinfo);
437
0
}
438
439
/*
440
 * List a directory (returns the filenames only)
441
 */
442
Datum
443
pg_ls_dir(PG_FUNCTION_ARGS)
444
0
{
445
0
  FuncCallContext *funcctx;
446
0
  struct dirent *de;
447
0
  directory_fctx *fctx;
448
0
  MemoryContext oldcontext;
449
450
0
  if (SRF_IS_FIRSTCALL())
451
0
  {
452
0
    bool    missing_ok = false;
453
0
    bool    include_dot_dirs = false;
454
455
    /* check the optional arguments */
456
0
    if (PG_NARGS() == 3)
457
0
    {
458
0
      if (!PG_ARGISNULL(1))
459
0
        missing_ok = PG_GETARG_BOOL(1);
460
0
      if (!PG_ARGISNULL(2))
461
0
        include_dot_dirs = PG_GETARG_BOOL(2);
462
0
    }
463
464
0
    funcctx = SRF_FIRSTCALL_INIT();
465
0
    oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
466
467
0
    fctx = palloc(sizeof(directory_fctx));
468
0
    fctx->location = convert_and_check_filename(PG_GETARG_TEXT_PP(0));
469
470
0
    fctx->include_dot_dirs = include_dot_dirs;
471
0
    fctx->dirdesc = AllocateDir(fctx->location);
472
473
0
    if (!fctx->dirdesc)
474
0
    {
475
0
      if (missing_ok && errno == ENOENT)
476
0
      {
477
0
        MemoryContextSwitchTo(oldcontext);
478
0
        SRF_RETURN_DONE(funcctx);
479
0
      }
480
0
      else
481
0
        ereport(ERROR,
482
0
            (errcode_for_file_access(),
483
0
             errmsg("could not open directory \"%s\": %m",
484
0
                fctx->location)));
485
0
    }
486
0
    funcctx->user_fctx = fctx;
487
0
    MemoryContextSwitchTo(oldcontext);
488
0
  }
489
490
0
  funcctx = SRF_PERCALL_SETUP();
491
0
  fctx = (directory_fctx *) funcctx->user_fctx;
492
493
0
  while ((de = ReadDir(fctx->dirdesc, fctx->location)) != NULL)
494
0
  {
495
0
    if (!fctx->include_dot_dirs &&
496
0
      (strcmp(de->d_name, ".") == 0 ||
497
0
       strcmp(de->d_name, "..") == 0))
498
0
      continue;
499
500
0
    SRF_RETURN_NEXT(funcctx, CStringGetTextDatum(de->d_name));
501
0
  }
502
503
0
  FreeDir(fctx->dirdesc);
504
505
0
  SRF_RETURN_DONE(funcctx);
506
0
}
507
508
/*
509
 * List a directory (1 argument version)
510
 *
511
 * note: this wrapper is necessary to pass the sanity check in opr_sanity,
512
 * which checks that all built-in functions that share the implementing C
513
 * function take the same number of arguments.
514
 */
515
Datum
516
pg_ls_dir_1arg(PG_FUNCTION_ARGS)
517
0
{
518
0
  return pg_ls_dir(fcinfo);
519
0
}
520
521
/* Generic function to return a directory listing of files */
522
static Datum
523
pg_ls_dir_files(FunctionCallInfo fcinfo, const char *dir)
524
2
{
525
2
  FuncCallContext *funcctx;
526
2
  struct dirent *de;
527
2
  directory_fctx *fctx;
528
529
2
  if (SRF_IS_FIRSTCALL())
530
1
  {
531
1
    MemoryContext oldcontext;
532
1
    TupleDesc tupdesc;
533
534
1
    funcctx = SRF_FIRSTCALL_INIT();
535
1
    oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
536
537
1
    fctx = palloc(sizeof(directory_fctx));
538
539
1
    tupdesc = CreateTemplateTupleDesc(3, false);
540
1
    TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name",
541
1
               TEXTOID, -1, 0);
542
1
    TupleDescInitEntry(tupdesc, (AttrNumber) 2, "size",
543
1
               INT8OID, -1, 0);
544
1
    TupleDescInitEntry(tupdesc, (AttrNumber) 3, "modification",
545
1
               TIMESTAMPTZOID, -1, 0);
546
1
    funcctx->tuple_desc = BlessTupleDesc(tupdesc);
547
548
1
    fctx->location = pstrdup(dir);
549
1
    fctx->dirdesc = AllocateDir(fctx->location);
550
551
1
    if (!fctx->dirdesc)
552
1
      ereport(ERROR,
553
1
          (errcode_for_file_access(),
554
1
           errmsg("could not open directory \"%s\": %m",
555
1
              fctx->location)));
556
557
1
    funcctx->user_fctx = fctx;
558
1
    MemoryContextSwitchTo(oldcontext);
559
1
  }
560
561
2
  funcctx = SRF_PERCALL_SETUP();
562
2
  fctx = (directory_fctx *) funcctx->user_fctx;
563
564
5
  while ((de = ReadDir(fctx->dirdesc, fctx->location)) != NULL)
565
4
  {
566
4
    Datum   values[3];
567
4
    bool    nulls[3];
568
4
    char    path[MAXPGPATH * 2];
569
4
    struct stat attrib;
570
4
    HeapTuple tuple;
571
572
    /* Skip hidden files */
573
4
    if (de->d_name[0] == '.')
574
2
      continue;
575
576
    /* Get the file info */
577
2
    snprintf(path, sizeof(path), "%s/%s", fctx->location, de->d_name);
578
2
    if (stat(path, &attrib) < 0)
579
2
      ereport(ERROR,
580
2
          (errcode_for_file_access(),
581
2
           errmsg("could not stat directory \"%s\": %m", dir)));
582
583
    /* Ignore anything but regular files */
584
2
    if (!S_ISREG(attrib.st_mode))
585
1
      continue;
586
587
1
    values[0] = CStringGetTextDatum(de->d_name);
588
1
    values[1] = Int64GetDatum((int64) attrib.st_size);
589
1
    values[2] = TimestampTzGetDatum(time_t_to_timestamptz(attrib.st_mtime));
590
1
    memset(nulls, 0, sizeof(nulls));
591
592
1
    tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);
593
1
    SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuple));
594
1
  }
595
596
1
  FreeDir(fctx->dirdesc);
597
1
  SRF_RETURN_DONE(funcctx);
598
1
}
599
600
/* Function to return the list of files in the log directory */
601
Datum
602
pg_ls_logdir(PG_FUNCTION_ARGS)
603
0
{
604
0
  return pg_ls_dir_files(fcinfo, Log_directory);
605
0
}
606
607
/* Function to return the list of files in the WAL directory */
608
Datum
609
pg_ls_waldir(PG_FUNCTION_ARGS)
610
2
{
611
2
  return pg_ls_dir_files(fcinfo, XLOGDIR);
612
2
}