Skip to content

Commit 89362fa

Browse files
committed
Added allow_non_convergent to pattern_matching promise in edit_line
Ticket: ENT-3417 Signed-off-by: Victor Moene <victor.moene@northern.tech>
1 parent 4bff865 commit 89362fa

File tree

2 files changed

+78
-18
lines changed

2 files changed

+78
-18
lines changed

cf-agent/files_editline.c

Lines changed: 77 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1257,12 +1257,32 @@ static bool DeletePromisedLinesMatching(EvalContext *ctx, Item **start, Item *be
12571257

12581258
/********************************************************************/
12591259

1260+
static bool ReplaceBufferSafeWrite(char *line_buff, char *after, Buffer *replace, int start_off, int end_off,
1261+
size_t after_size, size_t line_buff_size)
1262+
{
1263+
int needed;
1264+
// Save portion of line after substitution:
1265+
needed = strlcpy(after, line_buff + end_off, after_size);
1266+
if (needed < 0 || (size_t) needed >= after_size)
1267+
{
1268+
return false;
1269+
}
1270+
needed = snprintf(line_buff + start_off, line_buff_size - start_off,
1271+
"%s%s", BufferData(replace), after);
1272+
if (needed < 0 || (size_t) needed >= (line_buff_size - start_off))
1273+
{
1274+
return false;
1275+
}
1276+
return true;
1277+
}
1278+
12601279
static bool ReplacePatterns(EvalContext *ctx, Item *file_start, Item *file_end, const Attributes *a,
12611280
const Promise *pp, EditContext *edcontext, PromiseResult *result)
12621281
{
12631282
assert(a != NULL);
12641283
assert(pp != NULL);
12651284
assert(edcontext != NULL);
1285+
bool allow_non_convergent = PromiseGetConstraintAsBoolean(ctx, "allow_non_convergent", pp);
12661286

12671287
char line_buff[CF_EXPANDSIZE];
12681288
char after[CF_BUFSIZE];
@@ -1313,14 +1333,13 @@ static bool ReplacePatterns(EvalContext *ctx, Item *file_start, Item *file_end,
13131333
Log(LOG_LEVEL_VERBOSE, "Verifying replacement of '%s' with '%s', cutoff %d", pp->promiser, BufferData(replace),
13141334
cutoff);
13151335

1316-
// Save portion of line after substitution:
1317-
strlcpy(after, line_buff + end_off, sizeof(after));
1318-
// TODO: gripe if that truncated !
1319-
1320-
// Substitute into line_buff:
1321-
snprintf(line_buff + start_off, sizeof(line_buff) - start_off,
1322-
"%s%s", BufferData(replace), after);
1323-
// TODO: gripe if that truncated or failed !
1336+
if (!ReplaceBufferSafeWrite(line_buff, after, replace, start_off, end_off, sizeof(after), sizeof(line_buff)))
1337+
{
1338+
RecordInterruption(ctx, pp, a, "Replacement string is too large. '%s' in '%s'",
1339+
a->column.column_separator, edcontext->filename);
1340+
*result = PromiseResultUpdate(*result, PROMISE_RESULT_INTERRUPTED);
1341+
break;
1342+
}
13241343
notfound = false;
13251344
replaced = true;
13261345

@@ -1330,17 +1349,40 @@ static bool ReplacePatterns(EvalContext *ctx, Item *file_start, Item *file_end,
13301349
break;
13311350
}
13321351
}
1333-
1352+
char line_buff_cp[CF_EXPANDSIZE];
13341353
if (NotAnchored(pp->promiser) && BlockTextMatch(ctx, pp->promiser, line_buff, &start_off, &end_off))
13351354
{
1336-
RecordInterruption(ctx, pp, a,
1337-
"Promised replacement '%s' on line '%s' for pattern '%s'"
1338-
" is not convergent while editing '%s'"
1339-
" (regular expression matches the replacement string)",
1340-
line_buff, ip->name, pp->promiser, edcontext->filename);
1341-
*result = PromiseResultUpdate(*result, PROMISE_RESULT_INTERRUPTED);
1342-
PromiseRef(LOG_LEVEL_ERR, pp);
1343-
break;
1355+
int needed = strlcpy(line_buff_cp, line_buff, sizeof(line_buff_cp));
1356+
if (needed < 0 || (size_t) needed >= sizeof(line_buff_cp))
1357+
{
1358+
RecordInterruption(ctx, pp, a, "Original string is too large. '%s' in '%s'",
1359+
a->column.column_separator, edcontext->filename);
1360+
*result = PromiseResultUpdate(*result, PROMISE_RESULT_INTERRUPTED);
1361+
break;
1362+
}
1363+
1364+
if (!ReplaceBufferSafeWrite(line_buff_cp, after, replace, start_off, end_off, sizeof(after), sizeof(line_buff_cp)))
1365+
{
1366+
RecordInterruption(ctx, pp, a,
1367+
"Promised replacement '%s' on line '%s' for pattern '%s'"
1368+
" is not convergent while editing '%s'"
1369+
" (regular expression matches the replacement string)",
1370+
line_buff, ip->name, pp->promiser, edcontext->filename);
1371+
*result = PromiseResultUpdate(*result, PROMISE_RESULT_INTERRUPTED);
1372+
break;
1373+
}
1374+
1375+
if (!allow_non_convergent || (strlen(line_buff) != strlen(line_buff_cp)))
1376+
{
1377+
RecordInterruption(ctx, pp, a,
1378+
"Promised replacement '%s' on line '%s' for pattern '%s'"
1379+
" is not convergent while editing '%s'"
1380+
" (regular expression matches the replacement string)",
1381+
line_buff, ip->name, pp->promiser, edcontext->filename);
1382+
*result = PromiseResultUpdate(*result, PROMISE_RESULT_INTERRUPTED);
1383+
PromiseRef(LOG_LEVEL_ERR, pp);
1384+
break;
1385+
}
13441386
}
13451387

13461388
if (!MakingChanges(ctx, pp, a, result, "replace pattern '%s' in '%s'", pp->promiser,
@@ -1366,8 +1408,25 @@ static bool ReplacePatterns(EvalContext *ctx, Item *file_start, Item *file_end,
13661408
break;
13671409
}
13681410

1369-
if (BlockTextMatch(ctx, pp->promiser, ip->name, &start_off, &end_off))
1411+
if (BlockTextMatch(ctx, pp->promiser, ip->name, &start_off, &end_off) && (!allow_non_convergent
1412+
|| (strlen(line_buff) != strlen(line_buff_cp))))
13701413
{
1414+
int needed = strlcpy(line_buff_cp, line_buff, sizeof(line_buff_cp));
1415+
if (needed < 0 || (size_t) needed >= sizeof(line_buff_cp))
1416+
{
1417+
RecordInterruption(ctx, pp, a, "Original string is too large. '%s' in '%s'",
1418+
a->column.column_separator, edcontext->filename);
1419+
*result = PromiseResultUpdate(*result, PROMISE_RESULT_INTERRUPTED);
1420+
break;
1421+
}
1422+
1423+
if (!ReplaceBufferSafeWrite(line_buff_cp, after, replace, start_off, end_off, sizeof(after), sizeof(line_buff_cp))) {
1424+
RecordInterruption(ctx, pp, a, "Replacement string is too large. '%s' in '%s'",
1425+
a->column.column_separator, edcontext->filename);
1426+
*result = PromiseResultUpdate(*result, PROMISE_RESULT_INTERRUPTED);
1427+
break;
1428+
}
1429+
13711430
RecordInterruption(ctx, pp, a,
13721431
"Promised replacement '%s' for pattern '%s' is not properly convergent while editing '%s'"
13731432
" (pattern still matches the end-state replacement string '%s', consider use"

libpromises/mod_files.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ static const ConstraintSyntax CF_COLUMN_BODIES[] =
131131
static const ConstraintSyntax CF_REPLACE_BODIES[] =
132132
{
133133
ConstraintSyntaxNewBody("replace_with", &replace_with_body, "Search-replace pattern", SYNTAX_STATUS_NORMAL),
134+
ConstraintSyntaxNewBool("allow_non_convergent", "Allow to use non-convergent regular expressions in replace_patterns. Defaults to false", SYNTAX_STATUS_NORMAL),
134135
ConstraintSyntaxNewNull()
135136
};
136137

0 commit comments

Comments
 (0)