From 36ef9b2c28bdabc51aac41f1df34aaceb8481d5c Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sun, 4 May 2008 10:17:16 +1000 Subject: [PATCH] Helper routines for saving errno in error paths. --- noerr/_info.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++ noerr/noerr.c | 29 ++++++++++++++++++++++++++ noerr/noerr.h | 22 ++++++++++++++++++++ noerr/test/run.c | 48 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 153 insertions(+) create mode 100644 noerr/_info.c create mode 100644 noerr/noerr.c create mode 100644 noerr/noerr.h create mode 100644 noerr/test/run.c diff --git a/noerr/_info.c b/noerr/_info.c new file mode 100644 index 00000000..96fbbcd1 --- /dev/null +++ b/noerr/_info.c @@ -0,0 +1,54 @@ +#include +#include +#include "config.h" + +/** + * noerr - routines for cleaning up without blatting errno + * + * It is a good idea to follow the standard C convention of setting errno in + * your own helper functions. Unfortunately, care must be taken in the error + * paths as most standard functions can (and do) overwrite errno, even if they + * succeed. + * + * Example: + * #include + * #include + * #include + * + * bool write_string_to_file(const char *file, const char *string) + * { + * int ret, fd = open(file, O_WRONLY|O_CREAT|O_EXCL, 0600); + * if (fd < 0) + * return false; + * ret = write(fd, string, strlen(string)); + * if (ret < 0) { + * // Preserve errno from write above. + * close_noerr(fd); + * unlink_noerr(file); + * return false; + * } + * if (close(fd) != 0) { + * // Again, preserve errno. + * unlink_noerr(file); + * return false; + * } + * // A short write means out of space. + * if (ret < strlen(string)) { + * unlink(file); + * errno = ENOSPC; + * return false; + * } + * return true; + * } + */ +int main(int argc, char *argv[]) +{ + if (argc != 2) + return 1; + + if (strcmp(argv[1], "depends") == 0) + /* Nothing. */ + return 0; + + return 1; +} diff --git a/noerr/noerr.c b/noerr/noerr.c new file mode 100644 index 00000000..d6df81f7 --- /dev/null +++ b/noerr/noerr.c @@ -0,0 +1,29 @@ +#include "noerr.h" +#include +#include + +int close_noerr(int fd) +{ + int saved_errno = errno, ret; + + if (close(fd) != 0) + ret = errno; + else + ret = 0; + + errno = saved_errno; + return ret; +} + +int unlink_noerr(const char *pathname) +{ + int saved_errno = errno, ret; + + if (unlink(pathname) != 0) + ret = errno; + else + ret = 0; + + errno = saved_errno; + return ret; +} diff --git a/noerr/noerr.h b/noerr/noerr.h new file mode 100644 index 00000000..559ba613 --- /dev/null +++ b/noerr/noerr.h @@ -0,0 +1,22 @@ +#ifndef NOERR_H +#define NOERR_H + +/** + * close_noerr - close without stomping errno. + * @fd: the file descriptor to close. + * + * errno is saved and restored across the call to close: if an error occurs, + * the resulting (non-zero) errno is returned. + */ +int close_noerr(int fd); + +/** + * unlink_noerr - unlink a file without stomping errno. + * @pathname: the path to unlink. + * + * errno is saved and restored across the call to unlink: if an error occurs, + * the resulting (non-zero) errno is returned. + */ +int unlink_noerr(const char *pathname); + +#endif /* NOERR_H */ diff --git a/noerr/test/run.c b/noerr/test/run.c new file mode 100644 index 00000000..0cdd6d02 --- /dev/null +++ b/noerr/test/run.c @@ -0,0 +1,48 @@ +#include "noerr/noerr.h" +#include "tap.h" +#include "noerr/noerr.c" +#include +#include +#include +#include +#include + +int main(int argc, char *argv[]) +{ + /* tempnam(3) is generally a bad idea, but OK here. */ + char *name = tempnam(NULL, "noerr"); + int fd; + + plan_tests(12); + /* Should fail to unlink. */ + ok1(unlink(name) != 0); + ok1(errno == ENOENT); + + /* This one should not set errno. */ + errno = 100; + ok1(unlink_noerr(name) == ENOENT); + ok1(errno == 100); + + /* Should fail to close. */ + ok1(close(-1) != 0); + ok1(errno == EBADF); + + /* This one should not set errno. */ + errno = 100; + ok1(close_noerr(-1) == EBADF); + ok1(errno == 100); + + /* Test successful close/unlink doesn't hit errno either. */ + fd = open(name, O_WRONLY|O_CREAT|O_EXCL, 0600); + assert(fd >= 0); + + errno = 100; + ok1(close_noerr(fd) == 0); + ok1(errno == 100); + + errno = 100; + ok1(unlink_noerr(name) == 0); + ok1(errno == 100); + + return exit_status(); +} -- 2.39.2