Skip to content

Commit 5c47c65

Browse files
committed
Refactor parse_filename_for_nontemp_relation to parse more.
Instead of returning the number of characters in the RelFileNumber, return the RelFileNumber itself. Continue to return the fork number, as before, and additionally return the segment number. parse_filename_for_nontemp_relation now rejects a RelFileNumber or segment number that begins with a leading zero. Before, we accepted such cases as relation filenames, but if we continued to do so after this change, the function might return the same values for two different files (e.g. 1234.5 and 001234.5 or 1234.005) which could be annoying for callers. Since we don't actually ever generate filenames with leading zeroes in the names, any such files that we find must have been created by something other than PostgreSQL, and it is therefore reasonable to treat them as non-relation files. Along the way, change unlogged_relation_entry to store a RelFileNumber rather than an OID. This update should have been made in 851f4cc, but it was overlooked. It's trivial to make the update as part of this commit, perhaps more trivial than it would have been without it, so do that. Patch by me, reviewed by David Steele. Discussion: http://postgr.es/m/CA+TgmoZNVeBzoqDL8xvr-nkaepq815jtDR4nJzPew7=3iEuM1g@mail.gmail.com
1 parent b6f1cca commit 5c47c65

File tree

3 files changed

+93
-64
lines changed

3 files changed

+93
-64
lines changed

src/backend/backup/basebackup.c

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1197,9 +1197,9 @@ sendDir(bbsink *sink, const char *path, int basepathlen, bool sizeonly,
11971197
{
11981198
int excludeIdx;
11991199
bool excludeFound;
1200-
ForkNumber relForkNum; /* Type of fork if file is a relation */
1201-
int relnumchars; /* Chars in filename that are the
1202-
* relnumber */
1200+
RelFileNumber relNumber;
1201+
ForkNumber relForkNum;
1202+
unsigned segno;
12031203

12041204
/* Skip special stuff */
12051205
if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0)
@@ -1249,23 +1249,20 @@ sendDir(bbsink *sink, const char *path, int basepathlen, bool sizeonly,
12491249

12501250
/* Exclude all forks for unlogged tables except the init fork */
12511251
if (isDbDir &&
1252-
parse_filename_for_nontemp_relation(de->d_name, &relnumchars,
1253-
&relForkNum))
1252+
parse_filename_for_nontemp_relation(de->d_name, &relNumber,
1253+
&relForkNum, &segno))
12541254
{
12551255
/* Never exclude init forks */
12561256
if (relForkNum != INIT_FORKNUM)
12571257
{
12581258
char initForkFile[MAXPGPATH];
1259-
char relNumber[OIDCHARS + 1];
12601259

12611260
/*
12621261
* If any other type of fork, check if there is an init fork
12631262
* with the same RelFileNumber. If so, the file can be
12641263
* excluded.
12651264
*/
1266-
memcpy(relNumber, de->d_name, relnumchars);
1267-
relNumber[relnumchars] = '\0';
1268-
snprintf(initForkFile, sizeof(initForkFile), "%s/%s_init",
1265+
snprintf(initForkFile, sizeof(initForkFile), "%s/%u_init",
12691266
path, relNumber);
12701267

12711268
if (lstat(initForkFile, &statbuf) == 0)

src/backend/storage/file/reinit.c

Lines changed: 84 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ static void ResetUnloggedRelationsInDbspaceDir(const char *dbspacedirname,
3131

3232
typedef struct
3333
{
34-
Oid reloid; /* hash key */
34+
RelFileNumber relnumber; /* hash key */
3535
} unlogged_relation_entry;
3636

3737
/*
@@ -195,23 +195,22 @@ ResetUnloggedRelationsInDbspaceDir(const char *dbspacedirname, int op)
195195
while ((de = ReadDir(dbspace_dir, dbspacedirname)) != NULL)
196196
{
197197
ForkNumber forkNum;
198-
int relnumchars;
198+
unsigned segno;
199199
unlogged_relation_entry ent;
200200

201201
/* Skip anything that doesn't look like a relation data file. */
202-
if (!parse_filename_for_nontemp_relation(de->d_name, &relnumchars,
203-
&forkNum))
202+
if (!parse_filename_for_nontemp_relation(de->d_name,
203+
&ent.relnumber,
204+
&forkNum, &segno))
204205
continue;
205206

206207
/* Also skip it unless this is the init fork. */
207208
if (forkNum != INIT_FORKNUM)
208209
continue;
209210

210211
/*
211-
* Put the OID portion of the name into the hash table, if it
212-
* isn't already.
212+
* Put the RelFileNumber into the hash table, if it isn't already.
213213
*/
214-
ent.reloid = atooid(de->d_name);
215214
(void) hash_search(hash, &ent, HASH_ENTER, NULL);
216215
}
217216

@@ -235,12 +234,13 @@ ResetUnloggedRelationsInDbspaceDir(const char *dbspacedirname, int op)
235234
while ((de = ReadDir(dbspace_dir, dbspacedirname)) != NULL)
236235
{
237236
ForkNumber forkNum;
238-
int relnumchars;
237+
unsigned segno;
239238
unlogged_relation_entry ent;
240239

241240
/* Skip anything that doesn't look like a relation data file. */
242-
if (!parse_filename_for_nontemp_relation(de->d_name, &relnumchars,
243-
&forkNum))
241+
if (!parse_filename_for_nontemp_relation(de->d_name,
242+
&ent.relnumber,
243+
&forkNum, &segno))
244244
continue;
245245

246246
/* We never remove the init fork. */
@@ -251,7 +251,6 @@ ResetUnloggedRelationsInDbspaceDir(const char *dbspacedirname, int op)
251251
* See whether the OID portion of the name shows up in the hash
252252
* table. If so, nuke it!
253253
*/
254-
ent.reloid = atooid(de->d_name);
255254
if (hash_search(hash, &ent, HASH_FIND, NULL))
256255
{
257256
snprintf(rm_path, sizeof(rm_path), "%s/%s",
@@ -285,14 +284,14 @@ ResetUnloggedRelationsInDbspaceDir(const char *dbspacedirname, int op)
285284
while ((de = ReadDir(dbspace_dir, dbspacedirname)) != NULL)
286285
{
287286
ForkNumber forkNum;
288-
int relnumchars;
289-
char relnumbuf[OIDCHARS + 1];
287+
RelFileNumber relNumber;
288+
unsigned segno;
290289
char srcpath[MAXPGPATH * 2];
291290
char dstpath[MAXPGPATH];
292291

293292
/* Skip anything that doesn't look like a relation data file. */
294-
if (!parse_filename_for_nontemp_relation(de->d_name, &relnumchars,
295-
&forkNum))
293+
if (!parse_filename_for_nontemp_relation(de->d_name, &relNumber,
294+
&forkNum, &segno))
296295
continue;
297296

298297
/* Also skip it unless this is the init fork. */
@@ -304,11 +303,12 @@ ResetUnloggedRelationsInDbspaceDir(const char *dbspacedirname, int op)
304303
dbspacedirname, de->d_name);
305304

306305
/* Construct destination pathname. */
307-
memcpy(relnumbuf, de->d_name, relnumchars);
308-
relnumbuf[relnumchars] = '\0';
309-
snprintf(dstpath, sizeof(dstpath), "%s/%s%s",
310-
dbspacedirname, relnumbuf, de->d_name + relnumchars + 1 +
311-
strlen(forkNames[INIT_FORKNUM]));
306+
if (segno == 0)
307+
snprintf(dstpath, sizeof(dstpath), "%s/%u",
308+
dbspacedirname, relNumber);
309+
else
310+
snprintf(dstpath, sizeof(dstpath), "%s/%u.%u",
311+
dbspacedirname, relNumber, segno);
312312

313313
/* OK, we're ready to perform the actual copy. */
314314
elog(DEBUG2, "copying %s to %s", srcpath, dstpath);
@@ -327,26 +327,27 @@ ResetUnloggedRelationsInDbspaceDir(const char *dbspacedirname, int op)
327327
dbspace_dir = AllocateDir(dbspacedirname);
328328
while ((de = ReadDir(dbspace_dir, dbspacedirname)) != NULL)
329329
{
330+
RelFileNumber relNumber;
330331
ForkNumber forkNum;
331-
int relnumchars;
332-
char relnumbuf[OIDCHARS + 1];
332+
unsigned segno;
333333
char mainpath[MAXPGPATH];
334334

335335
/* Skip anything that doesn't look like a relation data file. */
336-
if (!parse_filename_for_nontemp_relation(de->d_name, &relnumchars,
337-
&forkNum))
336+
if (!parse_filename_for_nontemp_relation(de->d_name, &relNumber,
337+
&forkNum, &segno))
338338
continue;
339339

340340
/* Also skip it unless this is the init fork. */
341341
if (forkNum != INIT_FORKNUM)
342342
continue;
343343

344344
/* Construct main fork pathname. */
345-
memcpy(relnumbuf, de->d_name, relnumchars);
346-
relnumbuf[relnumchars] = '\0';
347-
snprintf(mainpath, sizeof(mainpath), "%s/%s%s",
348-
dbspacedirname, relnumbuf, de->d_name + relnumchars + 1 +
349-
strlen(forkNames[INIT_FORKNUM]));
345+
if (segno == 0)
346+
snprintf(mainpath, sizeof(mainpath), "%s/%u",
347+
dbspacedirname, relNumber);
348+
else
349+
snprintf(mainpath, sizeof(mainpath), "%s/%u.%u",
350+
dbspacedirname, relNumber, segno);
350351

351352
fsync_fname(mainpath, false);
352353
}
@@ -371,52 +372,82 @@ ResetUnloggedRelationsInDbspaceDir(const char *dbspacedirname, int op)
371372
* This function returns true if the file appears to be in the correct format
372373
* for a non-temporary relation and false otherwise.
373374
*
374-
* NB: If this function returns true, the caller is entitled to assume that
375-
* *relnumchars has been set to a value no more than OIDCHARS, and thus
376-
* that a buffer of OIDCHARS+1 characters is sufficient to hold the
377-
* RelFileNumber portion of the filename. This is critical to protect against
378-
* a possible buffer overrun.
375+
* If it returns true, it sets *relnumber, *fork, and *segno to the values
376+
* extracted from the filename. If it returns false, these values are set to
377+
* InvalidRelFileNumber, InvalidForkNumber, and 0, respectively.
379378
*/
380379
bool
381-
parse_filename_for_nontemp_relation(const char *name, int *relnumchars,
382-
ForkNumber *fork)
380+
parse_filename_for_nontemp_relation(const char *name, RelFileNumber *relnumber,
381+
ForkNumber *fork, unsigned *segno)
383382
{
384-
int pos;
383+
unsigned long n,
384+
s;
385+
ForkNumber f;
386+
char *endp;
385387

386-
/* Look for a non-empty string of digits (that isn't too long). */
387-
for (pos = 0; isdigit((unsigned char) name[pos]); ++pos)
388-
;
389-
if (pos == 0 || pos > OIDCHARS)
388+
*relnumber = InvalidRelFileNumber;
389+
*fork = InvalidForkNumber;
390+
*segno = 0;
391+
392+
/*
393+
* Relation filenames should begin with a digit that is not a zero. By
394+
* rejecting cases involving leading zeroes, the caller can assume that
395+
* there's only one possible string of characters that could have produced
396+
* any given value for *relnumber.
397+
*
398+
* (To be clear, we don't expect files with names like 0017.3 to exist at
399+
* all -- but if 0017.3 does exist, it's a non-relation file, not part of
400+
* the main fork for relfilenode 17.)
401+
*/
402+
if (name[0] < '1' || name[0] > '9')
403+
return false;
404+
405+
/*
406+
* Parse the leading digit string. If the value is out of range, we
407+
* conclude that this isn't a relation file at all.
408+
*/
409+
errno = 0;
410+
n = strtoul(name, &endp, 10);
411+
if (errno || name == endp || n <= 0 || n > PG_UINT32_MAX)
390412
return false;
391-
*relnumchars = pos;
413+
name = endp;
392414

393415
/* Check for a fork name. */
394-
if (name[pos] != '_')
395-
*fork = MAIN_FORKNUM;
416+
if (*name != '_')
417+
f = MAIN_FORKNUM;
396418
else
397419
{
398420
int forkchar;
399421

400-
forkchar = forkname_chars(&name[pos + 1], fork);
422+
forkchar = forkname_chars(name + 1, &f);
401423
if (forkchar <= 0)
402424
return false;
403-
pos += forkchar + 1;
425+
name += forkchar + 1;
404426
}
405427

406428
/* Check for a segment number. */
407-
if (name[pos] == '.')
429+
if (*name != '.')
430+
s = 0;
431+
else
408432
{
409-
int segchar;
433+
/* Reject leading zeroes, just like we do for RelFileNumber. */
434+
if (name[0] < '1' || name[0] > '9')
435+
return false;
410436

411-
for (segchar = 1; isdigit((unsigned char) name[pos + segchar]); ++segchar)
412-
;
413-
if (segchar <= 1)
437+
errno = 0;
438+
s = strtoul(name + 1, &endp, 10);
439+
if (errno || name + 1 == endp || s <= 0 || s > PG_UINT32_MAX)
414440
return false;
415-
pos += segchar;
441+
name = endp;
416442
}
417443

418444
/* Now we should be at the end. */
419-
if (name[pos] != '\0')
445+
if (*name != '\0')
420446
return false;
447+
448+
/* Set out parameters and return. */
449+
*relnumber = (RelFileNumber) n;
450+
*fork = f;
451+
*segno = (unsigned) s;
421452
return true;
422453
}

src/include/storage/reinit.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,9 @@
2020

2121
extern void ResetUnloggedRelations(int op);
2222
extern bool parse_filename_for_nontemp_relation(const char *name,
23-
int *relnumchars,
24-
ForkNumber *fork);
23+
RelFileNumber *relnumber,
24+
ForkNumber *fork,
25+
unsigned *segno);
2526

2627
#define UNLOGGED_RELATION_CLEANUP 0x0001
2728
#define UNLOGGED_RELATION_INIT 0x0002

0 commit comments

Comments
 (0)