libdrgn: add read(2) and pread(2) wrappers that don't return short reads

We have a couple of loops that deal with short reads/EINTR from read(2)
and pread(2), and upcoming changes would need to add more. Add some
wrappers to abstract this away.

drgn_read_memory_file() still needs the loop so it can fault on the
exact offset that returns EIO.

Signed-off-by: Omar Sandoval <osandov@osandov.com>
This commit is contained in:
Omar Sandoval 2022-08-26 10:10:47 -07:00
parent 56fda2a0cf
commit b8cdfff250
6 changed files with 96 additions and 37 deletions

View File

@ -269,7 +269,7 @@ TAB_SIZE = 4
# commands \{ and \} for these it is advised to use the version @{ and @} or use
# a double escape (\\{ and \\})
ALIASES =
ALIASES = manpage{2}="<a href=\"http://man7.org/linux/man-pages/man\2/\1.\2.html\">\1(\2)</a>"
# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
# only. Doxygen will then generate output that is more tailored for C. For

View File

@ -55,6 +55,8 @@ libdrgnimpl_la_SOURCES = $(ARCH_DEFS_PYS:_defs.py=.c) \
hash_table.c \
hash_table.h \
helpers.h \
io.c \
io.h \
language.c \
language.h \
language_c.c \

50
libdrgn/io.c Normal file
View File

@ -0,0 +1,50 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
// SPDX-License-Identifier: GPL-3.0-or-later
#include <errno.h>
#include <limits.h>
#include <unistd.h>
#include "io.h"
ssize_t read_all(int fd, void *buf, size_t count)
{
if (count > SSIZE_MAX) {
errno = EINVAL;
return -1;
}
size_t n = 0;
while (n < count) {
ssize_t r = read(fd, (char *)buf + n, count - n);
if (r < 0) {
if (errno == EINTR)
continue;
return r;
} else if (r == 0) {
break;
}
n += r;
}
return n;
}
ssize_t pread_all(int fd, void *buf, size_t count, off_t offset)
{
if (count > SSIZE_MAX) {
errno = EINVAL;
return -1;
}
size_t n = 0;
while (n < count) {
ssize_t r = pread(fd, (char *)buf + n, count - n, offset + n);
if (r < 0) {
if (errno == EINTR)
continue;
return r;
} else if (r == 0) {
break;
}
n += r;
}
return n;
}

27
libdrgn/io.h Normal file
View File

@ -0,0 +1,27 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
// SPDX-License-Identifier: GPL-3.0-or-later
/**
* @file
*
* Input/output helpers.
*/
#ifndef DRGN_IO_H
#define DRGN_IO_H
#include <sys/types.h>
/**
* Wrapper around \manpage{read,2} that never returns less bytes than requested unless it
* hits end-of-file.
*/
ssize_t read_all(int fd, void *buf, size_t count);
/**
* Wrapper around \manpage{pread,2} that never returns less bytes than requested unless
* it hits end-of-file.
*/
ssize_t pread_all(int fd, void *buf, size_t count, off_t offset);
#endif /* DRGN_IO_H */

View File

@ -23,6 +23,7 @@
#include "error.h"
#include "hash_table.h"
#include "helpers.h"
#include "io.h"
#include "linux_kernel.h"
#include "platform.h"
#include "program.h"
@ -630,27 +631,17 @@ kernel_module_iterator_gnu_build_id_live(struct kernel_module_iterator *it,
goto out;
}
char *buf = it->build_id_buf;
size_t size = 0;
while (size < st.st_size) {
ssize_t r = read(fd, buf + size, st.st_size - size);
ssize_t r = read_all(fd, it->build_id_buf, st.st_size);
if (r < 0) {
if (errno == EINTR)
continue;
err = drgn_error_format_os("read", errno,
"%s/%s", path,
err = drgn_error_format_os("read", errno, "%s/%s", path,
ent->d_name);
close(fd);
goto out;
} else if (r == 0) {
break;
}
size += r;
}
close(fd);
*build_id_len_ret = parse_gnu_build_id_from_note(buf, size,
false,
*build_id_len_ret =
parse_gnu_build_id_from_note(it->build_id_buf, r, false,
build_id_ret);
if (*build_id_len_ret) {
err = NULL;

View File

@ -21,6 +21,7 @@
#include "debug_info.h"
#include "error.h"
#include "helpers.h"
#include "io.h"
#include "language.h"
#include "linux_kernel.h"
#include "memory_reader.h"
@ -205,23 +206,11 @@ static struct drgn_error *has_kdump_signature(const char *path, int fd,
bool *ret)
{
char signature[KDUMP_SIG_LEN];
size_t n = 0;
while (n < sizeof(signature)) {
ssize_t sret;
sret = pread(fd, signature + n, sizeof(signature) - n, n);
if (sret == -1) {
if (errno == EINTR)
continue;
ssize_t r = pread_all(fd, signature, sizeof(signature), 0);
if (r < 0)
return drgn_error_create_os("pread", errno, path);
} else if (sret == 0) {
*ret = false;
return NULL;
}
n += sret;
}
*ret = memcmp(signature, KDUMP_SIGNATURE, sizeof(signature)) == 0;
*ret = (r == sizeof(signature)
&& memcmp(signature, KDUMP_SIGNATURE, sizeof(signature)) == 0);
return NULL;
}