]> git.ozlabs.org Git - ccan/commitdiff
Helper routines for saving errno in error paths.
authorRusty Russell <rusty@vivaldi>
Sun, 4 May 2008 00:17:16 +0000 (10:17 +1000)
committerRusty Russell <rusty@vivaldi>
Sun, 4 May 2008 00:17:16 +0000 (10:17 +1000)
noerr/_info.c [new file with mode: 0644]
noerr/noerr.c [new file with mode: 0644]
noerr/noerr.h [new file with mode: 0644]
noerr/test/run.c [new file with mode: 0644]

diff --git a/noerr/_info.c b/noerr/_info.c
new file mode 100644 (file)
index 0000000..96fbbcd
--- /dev/null
@@ -0,0 +1,54 @@
+#include <stdio.h>
+#include <string.h>
+#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 <sys/types.h>
+ *     #include <sys/stat.h>
+ *     #include <fcntl.h>
+ *
+ *     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 (file)
index 0000000..d6df81f
--- /dev/null
@@ -0,0 +1,29 @@
+#include "noerr.h"
+#include <unistd.h>
+#include <errno.h>
+
+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 (file)
index 0000000..559ba61
--- /dev/null
@@ -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 (file)
index 0000000..0cdd6d0
--- /dev/null
@@ -0,0 +1,48 @@
+#include "noerr/noerr.h"
+#include "tap.h"
+#include "noerr/noerr.c"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <assert.h>
+
+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();
+}