aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--check_kernel_version.c45
-rw-r--r--config.mk2
-rw-r--r--internal.h4
-rw-r--r--liberror-libc.h10
-rw-r--r--lseek.c121
6 files changed, 182 insertions, 2 deletions
diff --git a/Makefile b/Makefile
index 9ce5639..a9812c1 100644
--- a/Makefile
+++ b/Makefile
@@ -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
+}
diff --git a/config.mk b/config.mk
index 9171a69..c9a4302 100644
--- a/config.mk
+++ b/config.mk
@@ -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
diff --git a/internal.h b/internal.h
index 9bc9a26..2605d21 100644
--- a/internal.h
+++ b/internal.h
@@ -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]);
diff --git a/lseek.c b/lseek.c
new file mode 100644
index 0000000..7b1dc90
--- /dev/null
+++ b/lseek.c
@@ -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;
+}