diff options
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | check_kernel_version.c | 45 | ||||
-rw-r--r-- | config.mk | 2 | ||||
-rw-r--r-- | internal.h | 4 | ||||
-rw-r--r-- | liberror-libc.h | 10 | ||||
-rw-r--r-- | lseek.c | 121 |
6 files changed, 182 insertions, 2 deletions
@@ -21,9 +21,11 @@ OBJ =\ abs.o\ calloc.o\ chdir.o\ + check_kernel_version.o\ imaxabs.o\ labs.o\ llabs.o\ + lseek.o\ malloc.o\ pipe.o\ putenv.o\ diff --git a/check_kernel_version.c b/check_kernel_version.c new file mode 100644 index 0000000..ef5dd88 --- /dev/null +++ b/check_kernel_version.c @@ -0,0 +1,45 @@ +/* See LICENSE file for copyright and license details. */ +#include "internal.h" + + +int +liberror_libc_check_kernel_version(long int major, long int minor, long int patch) +{ +#if defined(__linux__) + + struct utsname name; + long int kmajor; + long int kminor; + long int kpatch; + char *p; + int r; + + r = uname(&name); + if (r < 0) + return -1; + + errno = 0; + p = name.release; + if (!isdigit(*p)) + return -1; + kmajor = strtol(p, &p, 10); + if (kmajor < 0 || errno || *p != '.') + return -1; + kminor = strtol(&p[1], &p, 10); + if (kminor < 0 || errno) + return -1; + if (*p != '.') { + kpatch = 0; + } else { + kpatch = strtol(&p[1], &p, 10); + if (kpatch < 0 || errno) + return -1; + } + + return kmajor != major ? kmajor > major : + kminor != minor ? kminor > minor : kpatch >= patch; + +#else +# error Unsupported kernel +#endif +} @@ -1,6 +1,6 @@ PREFIX = /usr MANPREFIX = $(PREFIX)/share/man -CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 +CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 CFLAGS = -std=c99 -Wall -pedantic LDFLAGS = -s -lerror @@ -3,6 +3,8 @@ #include <netinet/in.h> #include <sys/socket.h> +#include <sys/utsname.h> +#include <ctype.h> #include <errno.h> #include <fcntl.h> #include <inttypes.h> @@ -22,5 +24,5 @@ #define HIDDEN GCC_ATTRIBUTES(__visibility__("hidden")) - HIDDEN void liberror_libc_set_error_one_file(const char *, const char *, const char *, int, const char *); +HIDDEN int liberror_libc_check_kernel_version(long int, long int, long int); diff --git a/liberror-libc.h b/liberror-libc.h index 3ca2b67..ffdc127 100644 --- a/liberror-libc.h +++ b/liberror-libc.h @@ -8,6 +8,11 @@ #define LIBERROR_LIBC_ERRROR_SHORT_READ -1 +#define LIBERROR_LIBC_ERRROR_SHORT_SEEK -2 + + +typedef int64_t _liberror_off_t; +typedef uint64_t _liberror_uoff_t; int liberror_abs(int); @@ -23,6 +28,11 @@ long int liberror_labs(long int); void liberror_labs_failed(long int); long long int liberror_llabs(long long int); void liberror_llabs_failed(long long int); +_liberror_off_t liberror_lseek(int, _liberror_off_t, int, const char *); +_liberror_off_t liberror_lseek_require(int, _liberror_off_t, int, _liberror_uoff_t min, _liberror_uoff_t max, const char *); +void liberror_lseek_failed(int, _liberror_off_t, int, const char *); +void liberror_lseek_short(int, _liberror_off_t, int, _liberror_uoff_t min, _liberror_uoff_t max, + _liberror_off_t returned, const char *); void *liberror_malloc(size_t); void liberror_malloc_failed(size_t); int liberror_pipe(int[2]); @@ -0,0 +1,121 @@ +/* See LICENSE file for copyright and license details. */ +#include "internal.h" + + +void +liberror_lseek_failed(int fd, _liberror_off_t offset, int whence, const char *fname) +{ + const char *desc; + int saved_errno = errno; + switch (saved_errno) { + case EBADF: + if (fd < 0) + desc = "Negative file descriptor number specified"; + else + desc = "Unassigned file descriptor number specified"; + break; + case ESPIPE: + desc = "Unseekable file type (e.g. pipe, socket, or FIFO)"; + break; + case EOVERFLOW: + desc = "The resulting offset cannot be represented in an off_t; " + "consider compiling with _FILE_OFFSET_BITS=64"; + break; + case EINVAL: + if (whence == SEEK_SET || whence == SEEK_END || whence == SEEK_CUR) { + if (offset < 0) + desc = "Attempted to seek to a position before the beginning of the file"; + else if (offset > 0) + desc = "Attempted to seek beyond the end of the device"; + else if (whence == SEEK_SET) + desc = "Failed, for unknown reason, to seek to the beginning of the file"; + else if (whence == SEEK_END) + desc = "Failed, for unknown reason, to seek to the end of the device"; + else + desc = "Failed, for unknown reason, to look up current file offset"; + } else { + desc = "Unsupported seek mode"; + if (whence == SEEK_DATA || whence == SEEK_HOLE) { +#if defined(__linux__) + if (!liberror_libc_check_kernel_version(3, 1, 0)) + desc = "Unsupported seek mode; requires Linux 3.1 or newer"; +#else +# error Unsupported kernel +#endif + } + } + break; + case ENXIO: + if (whence == SEEK_DATA) { + if (offset < 0) { + desc = "Attempted seek for data beginning at an offset before the beginning of the file"; + } else { + desc = "Seeking for data from a hold at the end of the file, or at or after the end of the file"; + } + } else if (whence == SEEK_HOLE) { + if (offset < 0) { + desc = "Attempted seek for a hole beginning at an offset before the beginning of the file"; + } else { + desc = "Attempted seek for a hole from the end of the file, or after it's end"; + } + } + break; + default: + desc = ""; + break; + } + liberror_libc_set_error_one_file(desc, "sockatmark", "Seekable file", fd, fname); + errno = saved_errno; +} + + +void +liberror_lseek_short(int fd, _liberror_off_t offset, int whence, _liberror_uoff_t min, + _liberror_uoff_t max, _liberror_off_t returned, const char *fname) +{ + const char *desc; + struct liberror_error *error; + int saved_errno = errno; + liberror_save_backtrace(NULL); + if (returned < 0 || (size_t)returned < min) { + desc = "Seek resulted in a new offset in an earlier position than expected"; + } else if (returned > max) { + desc = "Seek resulted in a new offset in an beyond the expectation"; + } else { + desc = "Seek resulted in an unexpected new offset"; + } + liberror_set_error(desc, "recv", "liberror-libc", LIBERROR_LIBC_ERRROR_SHORT_SEEK); + error = liberror_get_error(); + error->details_type = LIBERROR_DETAILS_ONE_FILE; + error->details.one_file.fd = fd; + error->details.one_file.name = fname ? strdup(fname) : NULL; + error->details.one_file.role = "Seekable file"; + errno = saved_errno; +} + + +_liberror_off_t +liberror_lseek(int fd, _liberror_off_t offset, int whence, const char *fname) +{ + _liberror_off_t r = lseek(fd, offset, whence); + if (r >= 0) + return r; + liberror_save_backtrace(NULL); + liberror_lseek_failed(fd, offset, whence, fname); + return -1; +} + + +_liberror_off_t +liberror_lseek_require(int fd, _liberror_off_t offset, int whence, _liberror_uoff_t min, _liberror_uoff_t max, const char *fname) +{ + _liberror_off_t r = lseek(fd, offset, whence); + if (r >= 0 && (_liberror_uoff_t)r >= min && (_liberror_uoff_t)r <= max) + return r; + liberror_save_backtrace(NULL); + if (r < 0) + liberror_lseek_failed(fd, offset, whence, fname); + else + liberror_lseek_short(fd, offset, whence, min, max, r, fname); + return -1; +} |