Skip to content

Commit b83c84e

Browse files
committed
Defer auplugin_fgets buffer compaction
Defer auplugin fgets buffer compaction until space is required, ensuring destroy/clear routines reset to the original base and maintaining EOF handling across allocation types. Added a deferred-compaction regression test for the reentrant auplugin_fgets API and invoked it from the test driver.
1 parent 8ed522a commit b83c84e

File tree

2 files changed

+78
-26
lines changed

2 files changed

+78
-26
lines changed

auplugin/auplugin-fgets.c

Lines changed: 41 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,11 @@
3737
* the buffer to find a string terminated with a '\n'. It terminates
3838
* the string with a 0 and returns it. It updates current to point
3939
* to where it left off. On the next read it starts there and tries to
40-
* find a '\n'. If it can't find one, it slides the buffer down and
41-
* fills as much as it can from the descriptor. If the descriptor
42-
* becomes invalid or there is an error reading, it makes eof true.
43-
* The variable eptr marks the end of the buffer. It never changes.
40+
* find a '\n'. If it can't find one, it advances the buffer pointer
41+
* and only compacts the unread data when there is no room left for
42+
* the next read. If the descriptor becomes invalid or there is an
43+
* error reading, it makes eof true. The variable eptr marks the end
44+
* of the buffer. It never changes.
4445
*/
4546

4647
#define BUF_SIZE 8192
@@ -82,10 +83,10 @@ struct auplugin_fgets_state *auplugin_fgets_init(void)
8283

8384
void auplugin_fgets_destroy(struct auplugin_fgets_state *st)
8485
{
85-
if (st->buffer != st->internal) {
86+
if (st->buffer != st->internal || st->orig != st->internal) {
8687
switch (st->mem_type) {
8788
case MEM_MALLOC:
88-
free(st->buffer);
89+
free(st->orig);
8990
break;
9091
case MEM_MMAP:
9192
case MEM_MMAP_FILE:
@@ -115,6 +116,7 @@ void auplugin_fgets_clear_r(struct auplugin_fgets_state *st)
115116
st->buffer = st->orig;
116117
st->current = st->eptr;
117118
} else {
119+
st->buffer = st->orig;
118120
st->buffer[0] = 0;
119121
st->current = st->buffer;
120122
}
@@ -155,25 +157,37 @@ int auplugin_fgets_r(struct auplugin_fgets_state *st, char *buf, size_t blen, in
155157
line_end = memchr(st->buffer, '\n', avail);
156158

157159
/* 2) If not, and we still can read more, pull in more data */
158-
if (line_end == NULL && !st->eof && st->current != st->eptr) {
159-
do {
160-
nread = read(fd, st->current, st->eptr - st->current);
161-
} while (nread < 0 && errno == EINTR);
162-
163-
if (nread < 0)
164-
return -1;
165-
166-
if (nread == 0)
167-
st->eof = 1;
168-
else {
169-
size_t got = (size_t)nread;
170-
st->current[got] = '\0';
171-
st->current += got;
172-
avail += got;
160+
if (line_end == NULL && !st->eof) {
161+
if (st->current == st->eptr && st->buffer != st->orig) {
162+
size_t used = (size_t)(st->current - st->buffer);
163+
164+
memmove(st->orig, st->buffer, used);
165+
st->buffer = st->orig;
166+
st->current = st->buffer + used;
167+
avail = used;
168+
*st->current = '\0';
173169
}
174170

175-
/* see if a newline arrived in that chunk */
176-
line_end = memchr(st->buffer, '\n', avail);
171+
if (st->current != st->eptr) {
172+
do {
173+
nread = read(fd, st->current, st->eptr - st->current);
174+
} while (nread < 0 && errno == EINTR);
175+
176+
if (nread < 0)
177+
return -1;
178+
179+
if (nread == 0)
180+
st->eof = 1;
181+
else {
182+
size_t got = (size_t)nread;
183+
st->current[got] = '\0';
184+
st->current += got;
185+
avail += got;
186+
}
187+
188+
/* see if a newline arrived in that chunk */
189+
line_end = memchr(st->buffer, '\n', avail);
190+
}
177191
}
178192

179193
/* 3) Do we now have enough to return? */
@@ -203,14 +217,15 @@ int auplugin_fgets_r(struct auplugin_fgets_state *st, char *buf, size_t blen, in
203217
buf[line_len] = '\0';
204218

205219
size_t remainder = avail - line_len;
206-
/* For MEM_MMAP_FILE, can't slide it down, so move buffer beginning */
220+
/* For MEM_MMAP_FILE we advance over the returned data permanently.
221+
* For other modes we defer compaction until there is no write room
222+
* left for the next read. */
207223
if (st->mem_type == MEM_MMAP_FILE) {
208224
st->buffer += line_len;
209225
if (st->buffer >= st->eptr)
210226
st->eof = 1;
211227
} else {
212-
if (remainder > 0)
213-
memmove(st->buffer, st->buffer + line_len, remainder);
228+
st->buffer += line_len;
214229
}
215230

216231
st->current = st->buffer + remainder;

auplugin/test/fgets_r_test.c

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,42 @@ static void test_basic_state(void)
4444
auplugin_fgets_destroy(st);
4545
}
4646

47+
static void test_deferred_compaction(void)
48+
{
49+
int fds[2];
50+
char buf[64];
51+
char custom[33];
52+
const char *line =
53+
"0123456789abcdef0123456789abcdefQRSTUVWX\n";
54+
auplugin_fgets_state_t *st;
55+
size_t line_len = strlen(line);
56+
size_t capacity = sizeof(custom) - 1;
57+
58+
assert(pipe(fds) == 0);
59+
st = auplugin_fgets_init();
60+
assert(st);
61+
assert(auplugin_setvbuf_r(st, custom, capacity, MEM_SELF_MANAGED) == 0);
62+
63+
assert(write(fds[1], line, line_len) == (ssize_t)line_len);
64+
close(fds[1]);
65+
66+
int len = auplugin_fgets_r(st, buf, sizeof(buf), fds[0]);
67+
assert(len == (int)capacity);
68+
assert(strncmp(buf, line, (size_t)len) == 0);
69+
assert(auplugin_fgets_eof_r(st) == 0);
70+
71+
len = auplugin_fgets_r(st, buf, sizeof(buf), fds[0]);
72+
assert(len == (int)(line_len - capacity));
73+
assert(strcmp(buf, line + capacity) == 0);
74+
75+
len = auplugin_fgets_r(st, buf, sizeof(buf), fds[0]);
76+
assert(len == 0);
77+
assert(auplugin_fgets_eof_r(st) == 1);
78+
79+
close(fds[0]);
80+
auplugin_fgets_destroy(st);
81+
}
82+
4783
static void test_mmap_file(void)
4884
{
4985
const char *srcdir = getenv("srcdir") ? getenv("srcdir") : ".";
@@ -85,6 +121,7 @@ static void test_mmap_file(void)
85121
int main(void)
86122
{
87123
test_basic_state();
124+
test_deferred_compaction();
88125
test_mmap_file();
89126
printf("audit-fgets_r tests: all passed\n");
90127
return 0;

0 commit comments

Comments
 (0)