tor-commits
Threads by month
- ----- 2025 -----
- July
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
June 2018
- 16 participants
- 2190 discussions

[tor/master] Split read_all and write_all into separate functions
by nickm@torproject.org 27 Jun '18
by nickm@torproject.org 27 Jun '18
27 Jun '18
commit 67135ca8e041ac922d1045fe833c8052e652e5e7
Author: Nick Mathewson <nickm(a)torproject.org>
Date: Wed Jun 27 10:47:09 2018 -0400
Split read_all and write_all into separate functions
---
src/common/util.c | 71 ++++++++++++++++++++++++++++++++++++++--------------
src/common/util.h | 13 ++++++++--
src/lib/fdio/fdio.c | 2 +-
src/lib/fdio/fdio.h | 2 +-
src/lib/log/torlog.c | 4 +--
5 files changed, 67 insertions(+), 25 deletions(-)
diff --git a/src/common/util.c b/src/common/util.c
index 8334dd7ae..2d426bb13 100644
--- a/src/common/util.c
+++ b/src/common/util.c
@@ -1076,22 +1076,17 @@ format_time_interval(char *out, size_t out_len, long interval)
* File helpers
* ===== */
-/** Write <b>count</b> bytes from <b>buf</b> to <b>fd</b>. <b>isSocket</b>
- * must be 1 if fd was returned by socket() or accept(), and 0 if fd
- * was returned by open(). Return the number of bytes written, or -1
- * on error. Only use if fd is a blocking fd. */
+/** Write <b>count</b> bytes from <b>buf</b> to <b>fd</b>. Return the number
+ * of bytes written, or -1 on error. Only use if fd is a blocking fd. */
ssize_t
-write_all(tor_socket_t fd, const char *buf, size_t count, int isSocket)
+write_all_to_fd(int fd, const char *buf, size_t count)
{
size_t written = 0;
ssize_t result;
raw_assert(count < SSIZE_MAX);
while (written != count) {
- if (isSocket)
- result = tor_socket_send(fd, buf+written, count-written, 0);
- else
- result = write((int)fd, buf+written, count-written);
+ result = write(fd, buf+written, count-written);
if (result<0)
return -1;
written += result;
@@ -1099,13 +1094,29 @@ write_all(tor_socket_t fd, const char *buf, size_t count, int isSocket)
return (ssize_t)count;
}
-/** Read from <b>fd</b> to <b>buf</b>, until we get <b>count</b> bytes
- * or reach the end of the file. <b>isSocket</b> must be 1 if fd
- * was returned by socket() or accept(), and 0 if fd was returned by
- * open(). Return the number of bytes read, or -1 on error. Only use
- * if fd is a blocking fd. */
+/** Write <b>count</b> bytes from <b>buf</b> to <b>sock</b>. Return the number
+ * of bytes written, or -1 on error. Only use if fd is a blocking fd. */
ssize_t
-read_all(tor_socket_t fd, char *buf, size_t count, int isSocket)
+write_all_to_socket(tor_socket_t fd, const char *buf, size_t count)
+{
+ size_t written = 0;
+ ssize_t result;
+ raw_assert(count < SSIZE_MAX);
+
+ while (written != count) {
+ result = tor_socket_send(fd, buf+written, count-written, 0);
+ if (result<0)
+ return -1;
+ written += result;
+ }
+ return (ssize_t)count;
+}
+
+/** Read from <b>fd</b> to <b>buf</b>, until we get <b>count</b> bytes or
+ * reach the end of the file. Return the number of bytes read, or -1 on
+ * error. Only use if fd is a blocking fd. */
+ssize_t
+read_all_from_fd(int fd, char *buf, size_t count)
{
size_t numread = 0;
ssize_t result;
@@ -1116,10 +1127,32 @@ read_all(tor_socket_t fd, char *buf, size_t count, int isSocket)
}
while (numread < count) {
- if (isSocket)
- result = tor_socket_recv(fd, buf+numread, count-numread, 0);
- else
- result = read((int)fd, buf+numread, count-numread);
+ result = read(fd, buf+numread, count-numread);
+ if (result<0)
+ return -1;
+ else if (result == 0)
+ break;
+ numread += result;
+ }
+ return (ssize_t)numread;
+}
+
+/** Read from <b>sock</b> to <b>buf</b>, until we get <b>count</b> bytes or
+ * reach the end of the file. Return the number of bytes read, or -1 on
+ * error. Only use if fd is a blocking fd. */
+ssize_t
+read_all_from_socket(tor_socket_t sock, char *buf, size_t count)
+{
+ size_t numread = 0;
+ ssize_t result;
+
+ if (count > SIZE_T_CEILING || count > SSIZE_MAX) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ while (numread < count) {
+ result = tor_socket_recv(sock, buf+numread, count-numread, 0);
if (result<0)
return -1;
else if (result == 0)
diff --git a/src/common/util.h b/src/common/util.h
index 26ee2d75a..1cb3e73b3 100644
--- a/src/common/util.h
+++ b/src/common/util.h
@@ -117,8 +117,17 @@ int parse_http_time(const char *buf, struct tm *tm);
int format_time_interval(char *out, size_t out_len, long interval);
/* File helpers */
-ssize_t write_all(tor_socket_t fd, const char *buf, size_t count,int isSocket);
-ssize_t read_all(tor_socket_t fd, char *buf, size_t count, int isSocket);
+ssize_t write_all_to_fd(int fd, const char *buf, size_t count);
+ssize_t write_all_to_socket(tor_socket_t fd, const char *buf, size_t count);
+ssize_t read_all_from_fd(int fd, char *buf, size_t count);
+ssize_t read_all_from_socket(tor_socket_t fd, char *buf, size_t count);
+
+#define write_all(fd, buf, count, isSock) \
+ ((isSock) ? write_all_to_socket((fd), (buf), (count)) \
+ : write_all_to_fd((int)(fd), (buf), (count)))
+#define read_all(fd, buf, count, isSock) \
+ ((isSock) ? read_all_from_socket((fd), (buf), (count)) \
+ : read_all_from_fd((int)(fd), (buf), (count)))
/** Status of an I/O stream. */
enum stream_status {
diff --git a/src/lib/fdio/fdio.c b/src/lib/fdio/fdio.c
index b62207432..21577e184 100644
--- a/src/lib/fdio/fdio.c
+++ b/src/lib/fdio/fdio.c
@@ -94,7 +94,7 @@ tor_ftruncate(int fd)
/** Minimal version of write_all, for use by logging. */
int
-write_all_to_fd(int fd, const char *buf, size_t count)
+write_all_to_fd_minimal(int fd, const char *buf, size_t count)
{
size_t written = 0;
raw_assert(count < SSIZE_MAX);
diff --git a/src/lib/fdio/fdio.h b/src/lib/fdio/fdio.h
index 8cc4a0465..0fb3ed1e6 100644
--- a/src/lib/fdio/fdio.h
+++ b/src/lib/fdio/fdio.h
@@ -12,6 +12,6 @@ off_t tor_fd_getpos(int fd);
int tor_fd_setpos(int fd, off_t pos);
int tor_fd_seekend(int fd);
int tor_ftruncate(int fd);
-int write_all_to_fd(int fd, const char *buf, size_t count);
+int write_all_to_fd_minimal(int fd, const char *buf, size_t count);
#endif /* !defined(TOR_FDIO_H) */
diff --git a/src/lib/log/torlog.c b/src/lib/log/torlog.c
index 5709dd819..b6088d3eb 100644
--- a/src/lib/log/torlog.c
+++ b/src/lib/log/torlog.c
@@ -346,7 +346,7 @@ log_tor_version(logfile_t *lf, int reset)
tor_snprintf(buf+n, sizeof(buf)-n,
"Tor %s opening %slog file.\n", VERSION, is_new?"new ":"");
}
- if (write_all_to_fd(lf->fd, buf, strlen(buf)) < 0) /* error */
+ if (write_all_to_fd_minimal(lf->fd, buf, strlen(buf)) < 0) /* error */
return -1; /* failed */
return 0;
}
@@ -560,7 +560,7 @@ logfile_deliver(logfile_t *lf, const char *buf, size_t msg_len,
lf->callback(severity, domain, msg_after_prefix);
}
} else {
- if (write_all_to_fd(lf->fd, buf, msg_len) < 0) { /* error */
+ if (write_all_to_fd_minimal(lf->fd, buf, msg_len) < 0) { /* error */
/* don't log the error! mark this log entry to be blown away, and
* continue. */
lf->seems_dead = 1;
1
0
commit 21136037182f55b5aef3730853e65fa2c7ebd722
Merge: b9b05e437 05040a9e8
Author: Nick Mathewson <nickm(a)torproject.org>
Date: Wed Jun 27 14:45:14 2018 -0400
Merge branch 'sandbox_refactor'
.gitignore | 2 ++
Makefile.am | 2 ++
src/common/compat.c | 2 +-
src/common/include.am | 3 ---
src/common/storagedir.c | 2 +-
src/common/util.c | 2 +-
src/include.am | 1 +
src/lib/crypt_ops/.may_include | 1 +
src/lib/crypt_ops/crypto.c | 1 -
src/lib/crypt_ops/crypto_rand.c | 2 +-
src/lib/sandbox/.may_include | 15 +++++++++++++++
src/lib/sandbox/include.am | 18 ++++++++++++++++++
src/{common => lib/sandbox}/linux_syscalls.inc | 0
src/{common => lib/sandbox}/sandbox.c | 12 +++++++-----
src/{common => lib/sandbox}/sandbox.h | 0
src/or/config.c | 2 +-
src/or/connection.c | 2 +-
src/or/dns.c | 2 +-
src/or/main.c | 2 +-
src/or/routerlist.c | 2 +-
src/or/routerparse.c | 2 +-
src/or/statefile.c | 2 +-
src/rust/build.rs | 1 +
src/test/test_options.c | 2 +-
src/tools/tor-resolve.c | 2 +-
25 files changed, 60 insertions(+), 22 deletions(-)
diff --cc .gitignore
index 390859f5e,3c4c91e04..13c5a6788
--- a/.gitignore
+++ b/.gitignore
@@@ -185,10 -185,10 +185,12 @@@ uptime-*.jso
/src/lib/libtor-malloc-testing.a
/src/lib/libtor-net.a
/src/lib/libtor-net-testing.a
+ /src/lib/libtor-sandbox.a
+ /src/lib/libtor-sandbox-testing.a
/src/lib/libtor-string.a
/src/lib/libtor-string-testing.a
+/src/lib/libtor-smartlist-core.a
+/src/lib/libtor-smartlist-core-testing.a
/src/lib/libtor-tls.a
/src/lib/libtor-tls-testing.a
/src/lib/libtor-trace.a
diff --cc Makefile.am
index c151d441c,97057048d..c10790c52
--- a/Makefile.am
+++ b/Makefile.am
@@@ -40,7 -40,7 +40,8 @@@ endi
# "Common" libraries used to link tor's utility code.
TOR_UTIL_LIBS = \
src/common/libor.a \
+ src/lib/libtor-sandbox.a \
+ src/lib/libtor-container.a \
src/lib/libtor-net.a \
src/lib/libtor-log.a \
src/lib/libtor-lock.a \
@@@ -57,7 -57,7 +58,8 @@@
# and tests)
TOR_UTIL_TESTING_LIBS = \
src/common/libor-testing.a \
+ src/lib/libtor-sandbox-testing.a \
+ src/lib/libtor-container-testing.a \
src/lib/libtor-net-testing.a \
src/lib/libtor-log-testing.a \
src/lib/libtor-lock-testing.a \
diff --cc src/include.am
index b6ef3cf16,5d28ea34e..54b6505e5
--- a/src/include.am
+++ b/src/include.am
@@@ -13,8 -13,8 +13,9 @@@ include src/lib/lock/include.a
include src/lib/log/include.am
include src/lib/malloc/include.am
include src/lib/net/include.am
+ include src/lib/sandbox/include.am
include src/lib/string/include.am
+include src/lib/smartlist_core/include.am
include src/lib/testsupport/include.am
include src/lib/tls/include.am
include src/lib/trace/include.am
1
0

27 Jun '18
commit 3d606dddb9a104466906069bd21acbdfb1270b58
Author: Nick Mathewson <nickm(a)torproject.org>
Date: Wed Jun 27 12:35:48 2018 -0400
fixup! Move format_win32_error into lib/log/
---
configure.ac | 2 +-
src/lib/log/include.am | 7 +++++--
2 files changed, 6 insertions(+), 3 deletions(-)
diff --git a/configure.ac b/configure.ac
index 6d112b8c2..c99f9eee9 100644
--- a/configure.ac
+++ b/configure.ac
@@ -422,7 +422,7 @@ AH_BOTTOM([
#endif
])
-
+AM_CONDITIONAL(WIN32, test "x$bwin32" = "xtrue")
AM_CONDITIONAL(BUILD_NT_SERVICES, test "x$bwin32" = "xtrue")
AM_CONDITIONAL(BUILD_LIBTORRUNNER, test "x$bwin32" != "xtrue")
diff --git a/src/lib/log/include.am b/src/lib/log/include.am
index 22b141d31..f0491b386 100644
--- a/src/lib/log/include.am
+++ b/src/lib/log/include.am
@@ -9,8 +9,11 @@ src_lib_libtor_log_a_SOURCES = \
src/lib/log/escape.c \
src/lib/log/ratelim.c \
src/lib/log/torlog.c \
- src/lib/log/util_bug.c \
- src/lib/log/win32err.c
+ src/lib/log/util_bug.c
+
+if WIN32
+src_lib_libtor_log_a_SOURCES += src/lib/log/win32err.c
+endif
src_lib_libtor_log_testing_a_SOURCES = \
$(src_lib_libtor_log_a_SOURCES)
1
0
commit 3246c114a20da44064f7fae8eb6b6712e708a700
Author: Nick Mathewson <nickm(a)torproject.org>
Date: Wed Jun 27 12:15:22 2018 -0400
Move format_win32_error into lib/log/
---
src/common/compat.c | 45 ----------------------------------------
src/common/compat.h | 6 +-----
src/lib/log/include.am | 6 ++++--
src/lib/log/win32err.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++
src/lib/log/win32err.h | 17 +++++++++++++++
5 files changed, 78 insertions(+), 52 deletions(-)
diff --git a/src/common/compat.c b/src/common/compat.c
index 9cc0b4f04..b462ee1b4 100644
--- a/src/common/compat.c
+++ b/src/common/compat.c
@@ -1934,51 +1934,6 @@ network_init(void)
return 0;
}
-#ifdef _WIN32
-/** Return a newly allocated string describing the windows system error code
- * <b>err</b>. Note that error codes are different from errno. Error codes
- * come from GetLastError() when a winapi call fails. errno is set only when
- * ANSI functions fail. Whee. */
-char *
-format_win32_error(DWORD err)
-{
- TCHAR *str = NULL;
- char *result;
- DWORD n;
-
- /* Somebody once decided that this interface was better than strerror(). */
- n = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
- FORMAT_MESSAGE_FROM_SYSTEM |
- FORMAT_MESSAGE_IGNORE_INSERTS,
- NULL, err,
- MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT),
- (LPVOID)&str,
- 0, NULL);
-
- if (str && n) {
-#ifdef UNICODE
- size_t len;
- if (n > 128*1024)
- len = (128 * 1024) * 2 + 1; /* This shouldn't be possible, but let's
- * make sure. */
- else
- len = n * 2 + 1;
- result = tor_malloc(len);
- wcstombs(result,str,len);
- result[len-1] = '\0';
-#else /* !(defined(UNICODE)) */
- result = tor_strdup(str);
-#endif /* defined(UNICODE) */
- } else {
- result = tor_strdup("<unformattable error>");
- }
- if (str) {
- LocalFree(str); /* LocalFree != free() */
- }
- return result;
-}
-#endif /* defined(_WIN32) */
-
#if defined(HW_PHYSMEM64)
/* This appears to be an OpenBSD thing */
#define INT64_HW_MEM HW_PHYSMEM64
diff --git a/src/common/compat.h b/src/common/compat.h
index efd436324..dd45f2246 100644
--- a/src/common/compat.h
+++ b/src/common/compat.h
@@ -50,6 +50,7 @@
#include "lib/string/compat_ctype.h"
#include "lib/string/compat_string.h"
#include "lib/string/printf.h"
+#include "lib/log/win32err.h"
#include "lib/net/socket.h"
#include "lib/net/ipv4.h"
#include "lib/net/ipv6.h"
@@ -250,11 +251,6 @@ int tor_mlockall(void);
#define MIN(a,b) ( ((a)>(b)) ? (b) : (a) )
#endif
-/* Platform-specific helpers. */
-#ifdef _WIN32
-char *format_win32_error(DWORD err);
-#endif
-
/*for some reason my compiler doesn't have these version flags defined
a nice homework assignment for someone one day is to define the rest*/
//these are the values as given on MSDN
diff --git a/src/lib/log/include.am b/src/lib/log/include.am
index 235c95fdf..22b141d31 100644
--- a/src/lib/log/include.am
+++ b/src/lib/log/include.am
@@ -9,7 +9,8 @@ src_lib_libtor_log_a_SOURCES = \
src/lib/log/escape.c \
src/lib/log/ratelim.c \
src/lib/log/torlog.c \
- src/lib/log/util_bug.c
+ src/lib/log/util_bug.c \
+ src/lib/log/win32err.c
src_lib_libtor_log_testing_a_SOURCES = \
$(src_lib_libtor_log_a_SOURCES)
@@ -23,4 +24,5 @@ noinst_HEADERS += \
src/lib/log/escape.h \
src/lib/log/ratelim.h \
src/lib/log/torlog.h \
- src/lib/log/util_bug.h
+ src/lib/log/util_bug.h \
+ src/lib/log/win32err.h
diff --git a/src/lib/log/win32err.c b/src/lib/log/win32err.c
new file mode 100644
index 000000000..4586c23c8
--- /dev/null
+++ b/src/lib/log/win32err.c
@@ -0,0 +1,56 @@
+/* Copyright (c) 2003-2004, Roger Dingledine
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#ifdef _WIN32
+#include "orconfig.h"
+#include "lib/log/win32err.h"
+#include "lib/malloc/util_malloc.h"
+
+#include <tchar.h>
+#include <windows.h>
+
+/** Return a newly allocated string describing the windows system error code
+ * <b>err</b>. Note that error codes are different from errno. Error codes
+ * come from GetLastError() when a winapi call fails. errno is set only when
+ * ANSI functions fail. Whee. */
+char *
+format_win32_error(DWORD err)
+{
+ TCHAR *str = NULL;
+ char *result;
+ DWORD n;
+
+ /* Somebody once decided that this interface was better than strerror(). */
+ n = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL, err,
+ MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT),
+ (LPVOID)&str,
+ 0, NULL);
+
+ if (str && n) {
+#ifdef UNICODE
+ size_t len;
+ if (n > 128*1024)
+ len = (128 * 1024) * 2 + 1; /* This shouldn't be possible, but let's
+ * make sure. */
+ else
+ len = n * 2 + 1;
+ result = tor_malloc(len);
+ wcstombs(result,str,len);
+ result[len-1] = '\0';
+#else /* !(defined(UNICODE)) */
+ result = tor_strdup(str);
+#endif /* defined(UNICODE) */
+ } else {
+ result = tor_strdup("<unformattable error>");
+ }
+ if (str) {
+ LocalFree(str); /* LocalFree != free() */
+ }
+ return result;
+}
+#endif /* defined(_WIN32) */
diff --git a/src/lib/log/win32err.h b/src/lib/log/win32err.h
new file mode 100644
index 000000000..61d3af57d
--- /dev/null
+++ b/src/lib/log/win32err.h
@@ -0,0 +1,17 @@
+/* Copyright (c) 2003-2004, Roger Dingledine
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#ifndef TOR_WIN32ERR_H
+#define TOR_WIN32ERR_H
+
+#include "orconfig.h"
+
+/* Platform-specific helpers. */
+#ifdef _WIN32
+#include <windef.h>
+char *format_win32_error(DWORD err);
+#endif
+
+#endif
1
0

[tor/master] Extract functions from compat.c and util.h into a new fs library
by nickm@torproject.org 27 Jun '18
by nickm@torproject.org 27 Jun '18
27 Jun '18
commit 1e2e0f7e461b4518b123c4050c3dbc786ac422c4
Author: Nick Mathewson <nickm(a)torproject.org>
Date: Wed Jun 27 09:40:21 2018 -0400
Extract functions from compat.c and util.h into a new fs library
---
.gitignore | 2 +
Makefile.am | 2 +
src/common/compat.c | 556 --------------------------
src/common/compat.h | 50 +--
src/common/util.c | 1020 -----------------------------------------------
src/common/util.h | 84 +---
src/include.am | 1 +
src/lib/fs/.may_include | 11 +
src/lib/fs/dir.c | 360 +++++++++++++++++
src/lib/fs/dir.h | 27 ++
src/lib/fs/files.c | 711 +++++++++++++++++++++++++++++++++
src/lib/fs/files.h | 100 +++++
src/lib/fs/include.am | 25 ++
src/lib/fs/mmap.c | 234 +++++++++++
src/lib/fs/mmap.h | 35 ++
src/lib/fs/path.c | 289 ++++++++++++++
src/lib/fs/path.h | 24 ++
src/lib/fs/userdb.c | 132 ++++++
src/lib/fs/userdb.h | 20 +
19 files changed, 1979 insertions(+), 1704 deletions(-)
diff --git a/.gitignore b/.gitignore
index 3c4c91e04..e38b9568c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -175,6 +175,8 @@ uptime-*.json
/src/lib/libtor-err-testing.a
/src/lib/libtor-fdio.a
/src/lib/libtor-fdio-testing.a
+/src/lib/libtor-fs.a
+/src/lib/libtor-fs-testing.a
/src/lib/libtor-intmath.a
/src/lib/libtor-intmath-testing.a
/src/lib/libtor-lock.a
diff --git a/Makefile.am b/Makefile.am
index 97057048d..f43b2ad1f 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -40,6 +40,7 @@ endif
# "Common" libraries used to link tor's utility code.
TOR_UTIL_LIBS = \
src/common/libor.a \
+ src/lib/libtor-fs.a \
src/lib/libtor-sandbox.a \
src/lib/libtor-net.a \
src/lib/libtor-log.a \
@@ -57,6 +58,7 @@ TOR_UTIL_LIBS = \
# and tests)
TOR_UTIL_TESTING_LIBS = \
src/common/libor-testing.a \
+ src/lib/libtor-fs-testing.a \
src/lib/libtor-sandbox-testing.a \
src/lib/libtor-net-testing.a \
src/lib/libtor-log-testing.a \
diff --git a/src/common/compat.c b/src/common/compat.c
index b462ee1b4..e26591776 100644
--- a/src/common/compat.c
+++ b/src/common/compat.c
@@ -131,267 +131,6 @@ SecureZeroMemory(PVOID ptr, SIZE_T cnt)
#include "lib/net/address.h"
#include "lib/sandbox/sandbox.h"
-/** As open(path, flags, mode), but return an fd with the close-on-exec mode
- * set. */
-int
-tor_open_cloexec(const char *path, int flags, unsigned mode)
-{
- int fd;
- const char *p = sandbox_intern_string(path);
-#ifdef O_CLOEXEC
- fd = open(p, flags|O_CLOEXEC, mode);
- if (fd >= 0)
- return fd;
- /* If we got an error, see if it is EINVAL. EINVAL might indicate that,
- * even though we were built on a system with O_CLOEXEC support, we
- * are running on one without. */
- if (errno != EINVAL)
- return -1;
-#endif /* defined(O_CLOEXEC) */
-
- log_debug(LD_FS, "Opening %s with flags %x", p, flags);
- fd = open(p, flags, mode);
-#ifdef FD_CLOEXEC
- if (fd >= 0) {
- if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) {
- log_warn(LD_FS,"Couldn't set FD_CLOEXEC: %s", strerror(errno));
- close(fd);
- return -1;
- }
- }
-#endif /* defined(FD_CLOEXEC) */
- return fd;
-}
-
-/** As fopen(path,mode), but ensures that the O_CLOEXEC bit is set on the
- * underlying file handle. */
-FILE *
-tor_fopen_cloexec(const char *path, const char *mode)
-{
- FILE *result = fopen(path, mode);
-#ifdef FD_CLOEXEC
- if (result != NULL) {
- if (fcntl(fileno(result), F_SETFD, FD_CLOEXEC) == -1) {
- log_warn(LD_FS,"Couldn't set FD_CLOEXEC: %s", strerror(errno));
- fclose(result);
- return NULL;
- }
- }
-#endif /* defined(FD_CLOEXEC) */
- return result;
-}
-
-/** As rename(), but work correctly with the sandbox. */
-int
-tor_rename(const char *path_old, const char *path_new)
-{
- log_debug(LD_FS, "Renaming %s to %s", path_old, path_new);
- return rename(sandbox_intern_string(path_old),
- sandbox_intern_string(path_new));
-}
-
-#if defined(HAVE_MMAP) || defined(RUNNING_DOXYGEN)
-/** Try to create a memory mapping for <b>filename</b> and return it. On
- * failure, return NULL. Sets errno properly, using ERANGE to mean
- * "empty file". Must only be called on trusted Tor-owned files, as changing
- * the underlying file's size causes unspecified behavior. */
-tor_mmap_t *
-tor_mmap_file(const char *filename)
-{
- int fd; /* router file */
- char *string;
- int result;
- tor_mmap_t *res;
- size_t size, filesize;
- struct stat st;
-
- tor_assert(filename);
-
- fd = tor_open_cloexec(filename, O_RDONLY, 0);
- if (fd<0) {
- int save_errno = errno;
- int severity = (errno == ENOENT) ? LOG_INFO : LOG_WARN;
- log_fn(severity, LD_FS,"Could not open \"%s\" for mmap(): %s",filename,
- strerror(errno));
- errno = save_errno;
- return NULL;
- }
-
- /* Get the size of the file */
- result = fstat(fd, &st);
- if (result != 0) {
- int save_errno = errno;
- log_warn(LD_FS,
- "Couldn't fstat opened descriptor for \"%s\" during mmap: %s",
- filename, strerror(errno));
- close(fd);
- errno = save_errno;
- return NULL;
- }
- size = filesize = (size_t)(st.st_size);
-
- if (st.st_size > SSIZE_T_CEILING || (off_t)size < st.st_size) {
- log_warn(LD_FS, "File \"%s\" is too large. Ignoring.",filename);
- errno = EFBIG;
- close(fd);
- return NULL;
- }
- if (!size) {
- /* Zero-length file. If we call mmap on it, it will succeed but
- * return NULL, and bad things will happen. So just fail. */
- log_info(LD_FS,"File \"%s\" is empty. Ignoring.",filename);
- errno = ERANGE;
- close(fd);
- return NULL;
- }
-
- string = mmap(0, size, PROT_READ, MAP_PRIVATE, fd, 0);
- close(fd);
- if (string == MAP_FAILED) {
- int save_errno = errno;
- log_warn(LD_FS,"Could not mmap file \"%s\": %s", filename,
- strerror(errno));
- errno = save_errno;
- return NULL;
- }
-
- res = tor_malloc_zero(sizeof(tor_mmap_t));
- res->data = string;
- res->size = filesize;
- res->mapping_size = size;
-
- return res;
-}
-/** Release storage held for a memory mapping; returns 0 on success,
- * or -1 on failure (and logs a warning). */
-int
-tor_munmap_file(tor_mmap_t *handle)
-{
- int res;
-
- if (handle == NULL)
- return 0;
-
- res = munmap((char*)handle->data, handle->mapping_size);
- if (res == 0) {
- /* munmap() succeeded */
- tor_free(handle);
- } else {
- log_warn(LD_FS, "Failed to munmap() in tor_munmap_file(): %s",
- strerror(errno));
- res = -1;
- }
-
- return res;
-}
-#elif defined(_WIN32)
-tor_mmap_t *
-tor_mmap_file(const char *filename)
-{
- TCHAR tfilename[MAX_PATH]= {0};
- tor_mmap_t *res = tor_malloc_zero(sizeof(tor_mmap_t));
- int empty = 0;
- HANDLE file_handle = INVALID_HANDLE_VALUE;
- DWORD size_low, size_high;
- uint64_t real_size;
- res->mmap_handle = NULL;
-#ifdef UNICODE
- mbstowcs(tfilename,filename,MAX_PATH);
-#else
- strlcpy(tfilename,filename,MAX_PATH);
-#endif
- file_handle = CreateFile(tfilename,
- GENERIC_READ, FILE_SHARE_READ,
- NULL,
- OPEN_EXISTING,
- FILE_ATTRIBUTE_NORMAL,
- 0);
-
- if (file_handle == INVALID_HANDLE_VALUE)
- goto win_err;
-
- size_low = GetFileSize(file_handle, &size_high);
-
- if (size_low == INVALID_FILE_SIZE && GetLastError() != NO_ERROR) {
- log_warn(LD_FS,"Error getting size of \"%s\".",filename);
- goto win_err;
- }
- if (size_low == 0 && size_high == 0) {
- log_info(LD_FS,"File \"%s\" is empty. Ignoring.",filename);
- empty = 1;
- goto err;
- }
- real_size = (((uint64_t)size_high)<<32) | size_low;
- if (real_size > SIZE_MAX) {
- log_warn(LD_FS,"File \"%s\" is too big to map; not trying.",filename);
- goto err;
- }
- res->size = real_size;
-
- res->mmap_handle = CreateFileMapping(file_handle,
- NULL,
- PAGE_READONLY,
- size_high,
- size_low,
- NULL);
- if (res->mmap_handle == NULL)
- goto win_err;
- res->data = (char*) MapViewOfFile(res->mmap_handle,
- FILE_MAP_READ,
- 0, 0, 0);
- if (!res->data)
- goto win_err;
-
- CloseHandle(file_handle);
- return res;
- win_err: {
- DWORD e = GetLastError();
- int severity = (e == ERROR_FILE_NOT_FOUND || e == ERROR_PATH_NOT_FOUND) ?
- LOG_INFO : LOG_WARN;
- char *msg = format_win32_error(e);
- log_fn(severity, LD_FS, "Couldn't mmap file \"%s\": %s", filename, msg);
- tor_free(msg);
- if (e == ERROR_FILE_NOT_FOUND || e == ERROR_PATH_NOT_FOUND)
- errno = ENOENT;
- else
- errno = EINVAL;
- }
- err:
- if (empty)
- errno = ERANGE;
- if (file_handle != INVALID_HANDLE_VALUE)
- CloseHandle(file_handle);
- tor_munmap_file(res);
- return NULL;
-}
-
-/* Unmap the file, and return 0 for success or -1 for failure */
-int
-tor_munmap_file(tor_mmap_t *handle)
-{
- if (handle == NULL)
- return 0;
-
- if (handle->data) {
- /* This is an ugly cast, but without it, "data" in struct tor_mmap_t would
- have to be redefined as non-const. */
- BOOL ok = UnmapViewOfFile( (LPVOID) handle->data);
- if (!ok) {
- log_warn(LD_FS, "Failed to UnmapViewOfFile() in tor_munmap_file(): %d",
- (int)GetLastError());
- }
- }
-
- if (handle->mmap_handle != NULL)
- CloseHandle(handle->mmap_handle);
- tor_free(handle);
-
- return 0;
-}
-#else
-#error "cannot implement tor_mmap_file"
-#endif /* defined(HAVE_MMAP) || ... || ... */
-
/** Given <b>hlen</b> bytes at <b>haystack</b> and <b>nlen</b> bytes at
* <b>needle</b>, return a pointer to the first occurrence of the needle
* within the haystack, or NULL if there is no such occurrence.
@@ -553,45 +292,6 @@ set_uint64(void *cp, uint64_t v)
memcpy(cp,&v,8);
}
-/**
- * Rename the file <b>from</b> to the file <b>to</b>. On Unix, this is
- * the same as rename(2). On windows, this removes <b>to</b> first if
- * it already exists.
- * Returns 0 on success. Returns -1 and sets errno on failure.
- */
-int
-replace_file(const char *from, const char *to)
-{
-#ifndef _WIN32
- return tor_rename(from, to);
-#else
- switch (file_status(to))
- {
- case FN_NOENT:
- break;
- case FN_FILE:
- case FN_EMPTY:
- if (unlink(to)) return -1;
- break;
- case FN_ERROR:
- return -1;
- case FN_DIR:
- errno = EISDIR;
- return -1;
- }
- return tor_rename(from,to);
-#endif /* !defined(_WIN32) */
-}
-
-/** Change <b>fname</b>'s modification time to now. */
-int
-touch_file(const char *fname)
-{
- if (utime(fname, NULL)!=0)
- return -1;
- return 0;
-}
-
/** Represents a lockfile on which we hold the lock. */
struct tor_lockfile_t {
/** Name of the file */
@@ -928,109 +628,6 @@ log_credential_status(void)
}
#endif /* !defined(_WIN32) */
-#ifndef _WIN32
-/** Cached struct from the last getpwname() call we did successfully. */
-static struct passwd *passwd_cached = NULL;
-
-/** Helper: copy a struct passwd object.
- *
- * We only copy the fields pw_uid, pw_gid, pw_name, pw_dir. Tor doesn't use
- * any others, and I don't want to run into incompatibilities.
- */
-static struct passwd *
-tor_passwd_dup(const struct passwd *pw)
-{
- struct passwd *new_pw = tor_malloc_zero(sizeof(struct passwd));
- if (pw->pw_name)
- new_pw->pw_name = tor_strdup(pw->pw_name);
- if (pw->pw_dir)
- new_pw->pw_dir = tor_strdup(pw->pw_dir);
- new_pw->pw_uid = pw->pw_uid;
- new_pw->pw_gid = pw->pw_gid;
-
- return new_pw;
-}
-
-#define tor_passwd_free(pw) \
- FREE_AND_NULL(struct passwd, tor_passwd_free_, (pw))
-
-/** Helper: free one of our cached 'struct passwd' values. */
-static void
-tor_passwd_free_(struct passwd *pw)
-{
- if (!pw)
- return;
-
- tor_free(pw->pw_name);
- tor_free(pw->pw_dir);
- tor_free(pw);
-}
-
-/** Wrapper around getpwnam() that caches result. Used so that we don't need
- * to give the sandbox access to /etc/passwd.
- *
- * The following fields alone will definitely be copied in the output: pw_uid,
- * pw_gid, pw_name, pw_dir. Other fields are not present in cached values.
- *
- * When called with a NULL argument, this function clears storage associated
- * with static variables it uses.
- **/
-const struct passwd *
-tor_getpwnam(const char *username)
-{
- struct passwd *pw;
-
- if (username == NULL) {
- tor_passwd_free(passwd_cached);
- passwd_cached = NULL;
- return NULL;
- }
-
- if ((pw = getpwnam(username))) {
- tor_passwd_free(passwd_cached);
- passwd_cached = tor_passwd_dup(pw);
- log_info(LD_GENERAL, "Caching new entry %s for %s",
- passwd_cached->pw_name, username);
- return pw;
- }
-
- /* Lookup failed */
- if (! passwd_cached || ! passwd_cached->pw_name)
- return NULL;
-
- if (! strcmp(username, passwd_cached->pw_name))
- return passwd_cached; // LCOV_EXCL_LINE - would need to make getpwnam flaky
-
- return NULL;
-}
-
-/** Wrapper around getpwnam() that can use cached result from
- * tor_getpwnam(). Used so that we don't need to give the sandbox access to
- * /etc/passwd.
- *
- * The following fields alone will definitely be copied in the output: pw_uid,
- * pw_gid, pw_name, pw_dir. Other fields are not present in cached values.
- */
-const struct passwd *
-tor_getpwuid(uid_t uid)
-{
- struct passwd *pw;
-
- if ((pw = getpwuid(uid))) {
- return pw;
- }
-
- /* Lookup failed */
- if (! passwd_cached)
- return NULL;
-
- if (uid == passwd_cached->pw_uid)
- return passwd_cached; // LCOV_EXCL_LINE - would need to make getpwnam flaky
-
- return NULL;
-}
-#endif /* !defined(_WIN32) */
-
/** Return true iff we were compiled with capability support, and capabilities
* seem to work. **/
int
@@ -1322,159 +919,6 @@ tor_disable_debugger_attach(void)
return r;
}
-#ifdef HAVE_PWD_H
-/** Allocate and return a string containing the home directory for the
- * user <b>username</b>. Only works on posix-like systems. */
-char *
-get_user_homedir(const char *username)
-{
- const struct passwd *pw;
- tor_assert(username);
-
- if (!(pw = tor_getpwnam(username))) {
- log_err(LD_CONFIG,"User \"%s\" not found.", username);
- return NULL;
- }
- return tor_strdup(pw->pw_dir);
-}
-#endif /* defined(HAVE_PWD_H) */
-
-/** Modify <b>fname</b> to contain the name of its parent directory. Doesn't
- * actually examine the filesystem; does a purely syntactic modification.
- *
- * The parent of the root director is considered to be iteself.
- *
- * Path separators are the forward slash (/) everywhere and additionally
- * the backslash (\) on Win32.
- *
- * Cuts off any number of trailing path separators but otherwise ignores
- * them for purposes of finding the parent directory.
- *
- * Returns 0 if a parent directory was successfully found, -1 otherwise (fname
- * did not have any path separators or only had them at the end).
- * */
-int
-get_parent_directory(char *fname)
-{
- char *cp;
- int at_end = 1;
- tor_assert(fname);
-#ifdef _WIN32
- /* If we start with, say, c:, then don't consider that the start of the path
- */
- if (fname[0] && fname[1] == ':') {
- fname += 2;
- }
-#endif /* defined(_WIN32) */
- /* Now we want to remove all path-separators at the end of the string,
- * and to remove the end of the string starting with the path separator
- * before the last non-path-separator. In perl, this would be
- * s#[/]*$##; s#/[^/]*$##;
- * on a unixy platform.
- */
- cp = fname + strlen(fname);
- at_end = 1;
- while (--cp >= fname) {
- int is_sep = (*cp == '/'
-#ifdef _WIN32
- || *cp == '\\'
-#endif
- );
- if (is_sep) {
- if (cp == fname) {
- /* This is the first separator in the file name; don't remove it! */
- cp[1] = '\0';
- return 0;
- }
- *cp = '\0';
- if (! at_end)
- return 0;
- } else {
- at_end = 0;
- }
- }
- return -1;
-}
-
-#ifndef _WIN32
-/** Return a newly allocated string containing the output of getcwd(). Return
- * NULL on failure. (We can't just use getcwd() into a PATH_MAX buffer, since
- * Hurd hasn't got a PATH_MAX.)
- */
-static char *
-alloc_getcwd(void)
-{
-#ifdef HAVE_GET_CURRENT_DIR_NAME
- /* Glibc makes this nice and simple for us. */
- char *cwd = get_current_dir_name();
- char *result = NULL;
- if (cwd) {
- /* We make a copy here, in case tor_malloc() is not malloc(). */
- result = tor_strdup(cwd);
- raw_free(cwd); // alias for free to avoid tripping check-spaces.
- }
- return result;
-#else /* !(defined(HAVE_GET_CURRENT_DIR_NAME)) */
- size_t size = 1024;
- char *buf = NULL;
- char *ptr = NULL;
-
- while (ptr == NULL) {
- buf = tor_realloc(buf, size);
- ptr = getcwd(buf, size);
-
- if (ptr == NULL && errno != ERANGE) {
- tor_free(buf);
- return NULL;
- }
-
- size *= 2;
- }
- return buf;
-#endif /* defined(HAVE_GET_CURRENT_DIR_NAME) */
-}
-#endif /* !defined(_WIN32) */
-
-/** Expand possibly relative path <b>fname</b> to an absolute path.
- * Return a newly allocated string, possibly equal to <b>fname</b>. */
-char *
-make_path_absolute(char *fname)
-{
-#ifdef _WIN32
- char *absfname_malloced = _fullpath(NULL, fname, 1);
-
- /* We don't want to assume that tor_free can free a string allocated
- * with malloc. On failure, return fname (it's better than nothing). */
- char *absfname = tor_strdup(absfname_malloced ? absfname_malloced : fname);
- if (absfname_malloced) raw_free(absfname_malloced);
-
- return absfname;
-#else /* !(defined(_WIN32)) */
- char *absfname = NULL, *path = NULL;
-
- tor_assert(fname);
-
- if (fname[0] == '/') {
- absfname = tor_strdup(fname);
- } else {
- path = alloc_getcwd();
- if (path) {
- tor_asprintf(&absfname, "%s/%s", path, fname);
- tor_free(path);
- } else {
- /* LCOV_EXCL_START Can't make getcwd fail. */
- /* If getcwd failed, the best we can do here is keep using the
- * relative path. (Perhaps / isn't readable by this UID/GID.) */
- log_warn(LD_GENERAL, "Unable to find current working directory: %s",
- strerror(errno));
- absfname = tor_strdup(fname);
- /* LCOV_EXCL_STOP */
- }
- }
- return absfname;
-#endif /* defined(_WIN32) */
-}
-
#ifndef HAVE__NSGETENVIRON
#ifndef HAVE_EXTERN_ENVIRON_DECLARED
/* Some platforms declare environ under some circumstances, others don't. */
diff --git a/src/common/compat.h b/src/common/compat.h
index dd45f2246..1379f95a7 100644
--- a/src/common/compat.h
+++ b/src/common/compat.h
@@ -55,31 +55,13 @@
#include "lib/net/ipv4.h"
#include "lib/net/ipv6.h"
#include "lib/net/resolve.h"
+#include "lib/fs/files.h"
+#include "lib/fs/mmap.h"
+#include "lib/fs/userdb.h"
#include <stdio.h>
#include <errno.h>
-/* ===== Compiler compatibility */
-
-/** Represents an mmaped file. Allocated via tor_mmap_file; freed with
- * tor_munmap_file. */
-typedef struct tor_mmap_t {
- const char *data; /**< Mapping of the file's contents. */
- size_t size; /**< Size of the file. */
-
- /* None of the fields below should be accessed from outside compat.c */
-#ifdef HAVE_MMAP
- size_t mapping_size; /**< Size of the actual mapping. (This is this file
- * size, rounded up to the nearest page.) */
-#elif defined _WIN32
- HANDLE mmap_handle;
-#endif /* defined(HAVE_MMAP) || ... */
-
-} tor_mmap_t;
-
-tor_mmap_t *tor_mmap_file(const char *filename) ATTR_NONNULL((1));
-int tor_munmap_file(tor_mmap_t *handle) ATTR_NONNULL((1));
-
const void *tor_memmem(const void *haystack, size_t hlen, const void *needle,
size_t nlen) ATTR_NONNULL((1,3));
static const void *tor_memstr(const void *haystack, size_t hlen,
@@ -145,26 +127,11 @@ struct tm *tor_gmtime_r(const time_t *timep, struct tm *result);
#endif /* !defined(timercmp) */
/* ===== File compatibility */
-int tor_open_cloexec(const char *path, int flags, unsigned mode);
-FILE *tor_fopen_cloexec(const char *path, const char *mode);
-int tor_rename(const char *path_old, const char *path_new);
-
-int replace_file(const char *from, const char *to);
-int touch_file(const char *fname);
-
typedef struct tor_lockfile_t tor_lockfile_t;
tor_lockfile_t *tor_lockfile_lock(const char *filename, int blocking,
int *locked_out);
void tor_lockfile_unlock(tor_lockfile_t *lockfile);
-int64_t tor_get_avail_disk_space(const char *path);
-
-#ifdef _WIN32
-#define PATH_SEPARATOR "\\"
-#else
-#define PATH_SEPARATOR "/"
-#endif
-
/* ===== Net compatibility */
MOCK_DECL(int,tor_gethostname,(char *name, size_t namelen));
@@ -218,17 +185,6 @@ int have_capability_support(void);
/** Flag for switch_id; see switch_id() for documentation */
#define SWITCH_ID_WARN_IF_NO_CAPS (1<<1)
int switch_id(const char *user, unsigned flags);
-#ifdef HAVE_PWD_H
-char *get_user_homedir(const char *username);
-#endif
-
-#ifndef _WIN32
-const struct passwd *tor_getpwnam(const char *username);
-const struct passwd *tor_getpwuid(uid_t uid);
-#endif
-
-int get_parent_directory(char *fname);
-char *make_path_absolute(char *fname);
char **get_environment(void);
diff --git a/src/common/util.c b/src/common/util.c
index 940f25e27..6233b8e4f 100644
--- a/src/common/util.c
+++ b/src/common/util.c
@@ -1076,851 +1076,10 @@ format_time_interval(char *out, size_t out_len, long interval)
* File helpers
* ===== */
-/** Write <b>count</b> bytes from <b>buf</b> to <b>fd</b>. Return the number
- * of bytes written, or -1 on error. Only use if fd is a blocking fd. */
-ssize_t
-write_all_to_fd(int fd, const char *buf, size_t count)
-{
- size_t written = 0;
- ssize_t result;
- raw_assert(count < SSIZE_MAX);
-
- while (written != count) {
- result = write(fd, buf+written, count-written);
- if (result<0)
- return -1;
- written += result;
- }
- return (ssize_t)count;
-}
-
-/** Read from <b>fd</b> to <b>buf</b>, until we get <b>count</b> bytes or
- * reach the end of the file. Return the number of bytes read, or -1 on
- * error. Only use if fd is a blocking fd. */
-ssize_t
-read_all_from_fd(int fd, char *buf, size_t count)
-{
- size_t numread = 0;
- ssize_t result;
-
- if (count > SIZE_T_CEILING || count > SSIZE_MAX) {
- errno = EINVAL;
- return -1;
- }
-
- while (numread < count) {
- result = read(fd, buf+numread, count-numread);
- if (result<0)
- return -1;
- else if (result == 0)
- break;
- numread += result;
- }
- return (ssize_t)numread;
-}
-
/*
* Filesystem operations.
*/
-/** Clean up <b>name</b> so that we can use it in a call to "stat". On Unix,
- * we do nothing. On Windows, we remove a trailing slash, unless the path is
- * the root of a disk. */
-static void
-clean_name_for_stat(char *name)
-{
-#ifdef _WIN32
- size_t len = strlen(name);
- if (!len)
- return;
- if (name[len-1]=='\\' || name[len-1]=='/') {
- if (len == 1 || (len==3 && name[1]==':'))
- return;
- name[len-1]='\0';
- }
-#else /* !(defined(_WIN32)) */
- (void)name;
-#endif /* defined(_WIN32) */
-}
-
-/** Wrapper for unlink() to make it mockable for the test suite; returns 0
- * if unlinking the file succeeded, -1 and sets errno if unlinking fails.
- */
-
-MOCK_IMPL(int,
-tor_unlink,(const char *pathname))
-{
- return unlink(pathname);
-}
-
-/** Return:
- * FN_ERROR if filename can't be read, is NULL, or is zero-length,
- * FN_NOENT if it doesn't exist,
- * FN_FILE if it is a non-empty regular file, or a FIFO on unix-like systems,
- * FN_EMPTY for zero-byte regular files,
- * FN_DIR if it's a directory, and
- * FN_ERROR for any other file type.
- * On FN_ERROR and FN_NOENT, sets errno. (errno is not set when FN_ERROR
- * is returned due to an unhandled file type.) */
-file_status_t
-file_status(const char *fname)
-{
- struct stat st;
- char *f;
- int r;
- if (!fname || strlen(fname) == 0) {
- return FN_ERROR;
- }
- f = tor_strdup(fname);
- clean_name_for_stat(f);
- log_debug(LD_FS, "stat()ing %s", f);
- r = stat(sandbox_intern_string(f), &st);
- tor_free(f);
- if (r) {
- if (errno == ENOENT) {
- return FN_NOENT;
- }
- return FN_ERROR;
- }
- if (st.st_mode & S_IFDIR) {
- return FN_DIR;
- } else if (st.st_mode & S_IFREG) {
- if (st.st_size > 0) {
- return FN_FILE;
- } else if (st.st_size == 0) {
- return FN_EMPTY;
- } else {
- return FN_ERROR;
- }
-#ifndef _WIN32
- } else if (st.st_mode & S_IFIFO) {
- return FN_FILE;
-#endif
- } else {
- return FN_ERROR;
- }
-}
-
-/** Check whether <b>dirname</b> exists and is private. If yes return 0.
- * If <b>dirname</b> does not exist:
- * - if <b>check</b>&CPD_CREATE, try to create it and return 0 on success.
- * - if <b>check</b>&CPD_CHECK, and we think we can create it, return 0.
- * - if <b>check</b>&CPD_CHECK is false, and the directory exists, return 0.
- * - otherwise, return -1.
- * If CPD_GROUP_OK is set, then it's okay if the directory
- * is group-readable, but in all cases we create the directory mode 0700.
- * If CPD_GROUP_READ is set, existing directory behaves as CPD_GROUP_OK and
- * if the directory is created it will use mode 0750 with group read
- * permission. Group read privileges also assume execute permission
- * as norm for directories. If CPD_CHECK_MODE_ONLY is set, then we don't
- * alter the directory permissions if they are too permissive:
- * we just return -1.
- * When effective_user is not NULL, check permissions against the given user
- * and its primary group.
- */
-MOCK_IMPL(int,
-check_private_dir,(const char *dirname, cpd_check_t check,
- const char *effective_user))
-{
- int r;
- struct stat st;
-
- tor_assert(dirname);
-
-#ifndef _WIN32
- int fd;
- const struct passwd *pw = NULL;
- uid_t running_uid;
- gid_t running_gid;
-
- /*
- * Goal is to harden the implementation by removing any
- * potential for race between stat() and chmod().
- * chmod() accepts filename as argument. If an attacker can move
- * the file between stat() and chmod(), a potential race exists.
- *
- * Several suggestions taken from:
- * https://developer.apple.com/library/mac/documentation/
- * Security/Conceptual/SecureCodingGuide/Articles/RaceConditions.html
- */
-
- /* Open directory.
- * O_NOFOLLOW to ensure that it does not follow symbolic links */
- fd = open(sandbox_intern_string(dirname), O_NOFOLLOW);
-
- /* Was there an error? Maybe the directory does not exist? */
- if (fd == -1) {
-
- if (errno != ENOENT) {
- /* Other directory error */
- log_warn(LD_FS, "Directory %s cannot be read: %s", dirname,
- strerror(errno));
- return -1;
- }
-
- /* Received ENOENT: Directory does not exist */
-
- /* Should we create the directory? */
- if (check & CPD_CREATE) {
- log_info(LD_GENERAL, "Creating directory %s", dirname);
- if (check & CPD_GROUP_READ) {
- r = mkdir(dirname, 0750);
- } else {
- r = mkdir(dirname, 0700);
- }
-
- /* check for mkdir() error */
- if (r) {
- log_warn(LD_FS, "Error creating directory %s: %s", dirname,
- strerror(errno));
- return -1;
- }
-
- /* we just created the directory. try to open it again.
- * permissions on the directory will be checked again below.*/
- fd = open(sandbox_intern_string(dirname), O_NOFOLLOW);
-
- if (fd == -1) {
- log_warn(LD_FS, "Could not reopen recently created directory %s: %s",
- dirname,
- strerror(errno));
- return -1;
- } else {
- close(fd);
- }
-
- } else if (!(check & CPD_CHECK)) {
- log_warn(LD_FS, "Directory %s does not exist.", dirname);
- return -1;
- }
-
- /* XXXX In the case where check==CPD_CHECK, we should look at the
- * parent directory a little harder. */
- return 0;
- }
-
- tor_assert(fd >= 0);
-
- //f = tor_strdup(dirname);
- //clean_name_for_stat(f);
- log_debug(LD_FS, "stat()ing %s", dirname);
- //r = stat(sandbox_intern_string(f), &st);
- r = fstat(fd, &st);
- if (r == -1) {
- log_warn(LD_FS, "fstat() on directory %s failed.", dirname);
- close(fd);
- return -1;
- }
- //tor_free(f);
-
- /* check that dirname is a directory */
- if (!(st.st_mode & S_IFDIR)) {
- log_warn(LD_FS, "%s is not a directory", dirname);
- close(fd);
- return -1;
- }
-
- if (effective_user) {
- /* Look up the user and group information.
- * If we have a problem, bail out. */
- pw = tor_getpwnam(effective_user);
- if (pw == NULL) {
- log_warn(LD_CONFIG, "Error setting configured user: %s not found",
- effective_user);
- close(fd);
- return -1;
- }
- running_uid = pw->pw_uid;
- running_gid = pw->pw_gid;
- } else {
- running_uid = getuid();
- running_gid = getgid();
- }
- if (st.st_uid != running_uid) {
- char *process_ownername = NULL, *file_ownername = NULL;
-
- {
- const struct passwd *pw_running = tor_getpwuid(running_uid);
- process_ownername = pw_running ? tor_strdup(pw_running->pw_name) :
- tor_strdup("<unknown>");
- }
-
- {
- const struct passwd *pw_stat = tor_getpwuid(st.st_uid);
- file_ownername = pw_stat ? tor_strdup(pw_stat->pw_name) :
- tor_strdup("<unknown>");
- }
-
- log_warn(LD_FS, "%s is not owned by this user (%s, %d) but by "
- "%s (%d). Perhaps you are running Tor as the wrong user?",
- dirname, process_ownername, (int)running_uid,
- file_ownername, (int)st.st_uid);
-
- tor_free(process_ownername);
- tor_free(file_ownername);
- close(fd);
- return -1;
- }
- if ( (check & (CPD_GROUP_OK|CPD_GROUP_READ))
- && (st.st_gid != running_gid) && (st.st_gid != 0)) {
- struct group *gr;
- char *process_groupname = NULL;
- gr = getgrgid(running_gid);
- process_groupname = gr ? tor_strdup(gr->gr_name) : tor_strdup("<unknown>");
- gr = getgrgid(st.st_gid);
-
- log_warn(LD_FS, "%s is not owned by this group (%s, %d) but by group "
- "%s (%d). Are you running Tor as the wrong user?",
- dirname, process_groupname, (int)running_gid,
- gr ? gr->gr_name : "<unknown>", (int)st.st_gid);
-
- tor_free(process_groupname);
- close(fd);
- return -1;
- }
- unsigned unwanted_bits = 0;
- if (check & (CPD_GROUP_OK|CPD_GROUP_READ)) {
- unwanted_bits = 0027;
- } else {
- unwanted_bits = 0077;
- }
- unsigned check_bits_filter = ~0;
- if (check & CPD_RELAX_DIRMODE_CHECK) {
- check_bits_filter = 0022;
- }
- if ((st.st_mode & unwanted_bits & check_bits_filter) != 0) {
- unsigned new_mode;
- if (check & CPD_CHECK_MODE_ONLY) {
- log_warn(LD_FS, "Permissions on directory %s are too permissive.",
- dirname);
- close(fd);
- return -1;
- }
- log_warn(LD_FS, "Fixing permissions on directory %s", dirname);
- new_mode = st.st_mode;
- new_mode |= 0700; /* Owner should have rwx */
- if (check & CPD_GROUP_READ) {
- new_mode |= 0050; /* Group should have rx */
- }
- new_mode &= ~unwanted_bits; /* Clear the bits that we didn't want set...*/
- if (fchmod(fd, new_mode)) {
- log_warn(LD_FS, "Could not chmod directory %s: %s", dirname,
- strerror(errno));
- close(fd);
- return -1;
- } else {
- close(fd);
- return 0;
- }
- }
- close(fd);
-#else /* !(!defined(_WIN32)) */
- /* Win32 case: we can't open() a directory. */
- (void)effective_user;
-
- char *f = tor_strdup(dirname);
- clean_name_for_stat(f);
- log_debug(LD_FS, "stat()ing %s", f);
- r = stat(sandbox_intern_string(f), &st);
- tor_free(f);
- if (r) {
- if (errno != ENOENT) {
- log_warn(LD_FS, "Directory %s cannot be read: %s", dirname,
- strerror(errno));
- return -1;
- }
- if (check & CPD_CREATE) {
- log_info(LD_GENERAL, "Creating directory %s", dirname);
- r = mkdir(dirname);
- if (r) {
- log_warn(LD_FS, "Error creating directory %s: %s", dirname,
- strerror(errno));
- return -1;
- }
- } else if (!(check & CPD_CHECK)) {
- log_warn(LD_FS, "Directory %s does not exist.", dirname);
- return -1;
- }
- return 0;
- }
- if (!(st.st_mode & S_IFDIR)) {
- log_warn(LD_FS, "%s is not a directory", dirname);
- return -1;
- }
-
-#endif /* !defined(_WIN32) */
- return 0;
-}
-
-/** Create a file named <b>fname</b> with the contents <b>str</b>. Overwrite
- * the previous <b>fname</b> if possible. Return 0 on success, -1 on failure.
- *
- * This function replaces the old file atomically, if possible. This
- * function, and all other functions in util.c that create files, create them
- * with mode 0600.
- */
-MOCK_IMPL(int,
-write_str_to_file,(const char *fname, const char *str, int bin))
-{
-#ifdef _WIN32
- if (!bin && strchr(str, '\r')) {
- log_warn(LD_BUG,
- "We're writing a text string that already contains a CR to %s",
- escaped(fname));
- }
-#endif /* defined(_WIN32) */
- return write_bytes_to_file(fname, str, strlen(str), bin);
-}
-
-/** Represents a file that we're writing to, with support for atomic commit:
- * we can write into a temporary file, and either remove the file on
- * failure, or replace the original file on success. */
-struct open_file_t {
- char *tempname; /**< Name of the temporary file. */
- char *filename; /**< Name of the original file. */
- unsigned rename_on_close:1; /**< Are we using the temporary file or not? */
- unsigned binary:1; /**< Did we open in binary mode? */
- int fd; /**< fd for the open file. */
- FILE *stdio_file; /**< stdio wrapper for <b>fd</b>. */
-};
-
-/** Try to start writing to the file in <b>fname</b>, passing the flags
- * <b>open_flags</b> to the open() syscall, creating the file (if needed) with
- * access value <b>mode</b>. If the O_APPEND flag is set, we append to the
- * original file. Otherwise, we open a new temporary file in the same
- * directory, and either replace the original or remove the temporary file
- * when we're done.
- *
- * Return the fd for the newly opened file, and store working data in
- * *<b>data_out</b>. The caller should not close the fd manually:
- * instead, call finish_writing_to_file() or abort_writing_to_file().
- * Returns -1 on failure.
- *
- * NOTE: When not appending, the flags O_CREAT and O_TRUNC are treated
- * as true and the flag O_EXCL is treated as false.
- *
- * NOTE: Ordinarily, O_APPEND means "seek to the end of the file before each
- * write()". We don't do that.
- */
-int
-start_writing_to_file(const char *fname, int open_flags, int mode,
- open_file_t **data_out)
-{
- open_file_t *new_file = tor_malloc_zero(sizeof(open_file_t));
- const char *open_name;
- int append = 0;
-
- tor_assert(fname);
- tor_assert(data_out);
-#if (O_BINARY != 0 && O_TEXT != 0)
- tor_assert((open_flags & (O_BINARY|O_TEXT)) != 0);
-#endif
- new_file->fd = -1;
- new_file->filename = tor_strdup(fname);
- if (open_flags & O_APPEND) {
- open_name = fname;
- new_file->rename_on_close = 0;
- append = 1;
- open_flags &= ~O_APPEND;
- } else {
- tor_asprintf(&new_file->tempname, "%s.tmp", fname);
- open_name = new_file->tempname;
- /* We always replace an existing temporary file if there is one. */
- open_flags |= O_CREAT|O_TRUNC;
- open_flags &= ~O_EXCL;
- new_file->rename_on_close = 1;
- }
-#if O_BINARY != 0
- if (open_flags & O_BINARY)
- new_file->binary = 1;
-#endif
-
- new_file->fd = tor_open_cloexec(open_name, open_flags, mode);
- if (new_file->fd < 0) {
- log_warn(LD_FS, "Couldn't open \"%s\" (%s) for writing: %s",
- open_name, fname, strerror(errno));
- goto err;
- }
- if (append) {
- if (tor_fd_seekend(new_file->fd) < 0) {
- log_warn(LD_FS, "Couldn't seek to end of file \"%s\": %s", open_name,
- strerror(errno));
- goto err;
- }
- }
-
- *data_out = new_file;
-
- return new_file->fd;
-
- err:
- if (new_file->fd >= 0)
- close(new_file->fd);
- *data_out = NULL;
- tor_free(new_file->filename);
- tor_free(new_file->tempname);
- tor_free(new_file);
- return -1;
-}
-
-/** Given <b>file_data</b> from start_writing_to_file(), return a stdio FILE*
- * that can be used to write to the same file. The caller should not mix
- * stdio calls with non-stdio calls. */
-FILE *
-fdopen_file(open_file_t *file_data)
-{
- tor_assert(file_data);
- if (file_data->stdio_file)
- return file_data->stdio_file;
- tor_assert(file_data->fd >= 0);
- if (!(file_data->stdio_file = fdopen(file_data->fd,
- file_data->binary?"ab":"a"))) {
- log_warn(LD_FS, "Couldn't fdopen \"%s\" [%d]: %s", file_data->filename,
- file_data->fd, strerror(errno));
- }
- return file_data->stdio_file;
-}
-
-/** Combines start_writing_to_file with fdopen_file(): arguments are as
- * for start_writing_to_file, but */
-FILE *
-start_writing_to_stdio_file(const char *fname, int open_flags, int mode,
- open_file_t **data_out)
-{
- FILE *res;
- if (start_writing_to_file(fname, open_flags, mode, data_out)<0)
- return NULL;
- if (!(res = fdopen_file(*data_out))) {
- abort_writing_to_file(*data_out);
- *data_out = NULL;
- }
- return res;
-}
-
-/** Helper function: close and free the underlying file and memory in
- * <b>file_data</b>. If we were writing into a temporary file, then delete
- * that file (if abort_write is true) or replaces the target file with
- * the temporary file (if abort_write is false). */
-static int
-finish_writing_to_file_impl(open_file_t *file_data, int abort_write)
-{
- int r = 0;
-
- tor_assert(file_data && file_data->filename);
- if (file_data->stdio_file) {
- if (fclose(file_data->stdio_file)) {
- log_warn(LD_FS, "Error closing \"%s\": %s", file_data->filename,
- strerror(errno));
- abort_write = r = -1;
- }
- } else if (file_data->fd >= 0 && close(file_data->fd) < 0) {
- log_warn(LD_FS, "Error flushing \"%s\": %s", file_data->filename,
- strerror(errno));
- abort_write = r = -1;
- }
-
- if (file_data->rename_on_close) {
- tor_assert(file_data->tempname && file_data->filename);
- if (!abort_write) {
- tor_assert(strcmp(file_data->filename, file_data->tempname));
- if (replace_file(file_data->tempname, file_data->filename)) {
- log_warn(LD_FS, "Error replacing \"%s\": %s", file_data->filename,
- strerror(errno));
- abort_write = r = -1;
- }
- }
- if (abort_write) {
- int res = unlink(file_data->tempname);
- if (res != 0) {
- /* We couldn't unlink and we'll leave a mess behind */
- log_warn(LD_FS, "Failed to unlink %s: %s",
- file_data->tempname, strerror(errno));
- r = -1;
- }
- }
- }
-
- tor_free(file_data->filename);
- tor_free(file_data->tempname);
- tor_free(file_data);
-
- return r;
-}
-
-/** Finish writing to <b>file_data</b>: close the file handle, free memory as
- * needed, and if using a temporary file, replace the original file with
- * the temporary file. */
-int
-finish_writing_to_file(open_file_t *file_data)
-{
- return finish_writing_to_file_impl(file_data, 0);
-}
-
-/** Finish writing to <b>file_data</b>: close the file handle, free memory as
- * needed, and if using a temporary file, delete it. */
-int
-abort_writing_to_file(open_file_t *file_data)
-{
- return finish_writing_to_file_impl(file_data, 1);
-}
-
-/** Helper: given a set of flags as passed to open(2), open the file
- * <b>fname</b> and write all the sized_chunk_t structs in <b>chunks</b> to
- * the file. Do so as atomically as possible e.g. by opening temp files and
- * renaming. */
-static int
-write_chunks_to_file_impl(const char *fname, const smartlist_t *chunks,
- int open_flags)
-{
- open_file_t *file = NULL;
- int fd;
- ssize_t result;
- fd = start_writing_to_file(fname, open_flags, 0600, &file);
- if (fd<0)
- return -1;
- SMARTLIST_FOREACH(chunks, sized_chunk_t *, chunk,
- {
- result = write_all(fd, chunk->bytes, chunk->len, 0);
- if (result < 0) {
- log_warn(LD_FS, "Error writing to \"%s\": %s", fname,
- strerror(errno));
- goto err;
- }
- tor_assert((size_t)result == chunk->len);
- });
-
- return finish_writing_to_file(file);
- err:
- abort_writing_to_file(file);
- return -1;
-}
-
-/** Given a smartlist of sized_chunk_t, write them to a file
- * <b>fname</b>, overwriting or creating the file as necessary.
- * If <b>no_tempfile</b> is 0 then the file will be written
- * atomically. */
-int
-write_chunks_to_file(const char *fname, const smartlist_t *chunks, int bin,
- int no_tempfile)
-{
- int flags = OPEN_FLAGS_REPLACE|(bin?O_BINARY:O_TEXT);
-
- if (no_tempfile) {
- /* O_APPEND stops write_chunks_to_file from using tempfiles */
- flags |= O_APPEND;
- }
- return write_chunks_to_file_impl(fname, chunks, flags);
-}
-
-/** Write <b>len</b> bytes, starting at <b>str</b>, to <b>fname</b>
- using the open() flags passed in <b>flags</b>. */
-static int
-write_bytes_to_file_impl(const char *fname, const char *str, size_t len,
- int flags)
-{
- int r;
- sized_chunk_t c = { str, len };
- smartlist_t *chunks = smartlist_new();
- smartlist_add(chunks, &c);
- r = write_chunks_to_file_impl(fname, chunks, flags);
- smartlist_free(chunks);
- return r;
-}
-
-/** As write_str_to_file, but does not assume a NUL-terminated
- * string. Instead, we write <b>len</b> bytes, starting at <b>str</b>. */
-MOCK_IMPL(int,
-write_bytes_to_file,(const char *fname, const char *str, size_t len,
- int bin))
-{
- return write_bytes_to_file_impl(fname, str, len,
- OPEN_FLAGS_REPLACE|(bin?O_BINARY:O_TEXT));
-}
-
-/** As write_bytes_to_file, but if the file already exists, append the bytes
- * to the end of the file instead of overwriting it. */
-int
-append_bytes_to_file(const char *fname, const char *str, size_t len,
- int bin)
-{
- return write_bytes_to_file_impl(fname, str, len,
- OPEN_FLAGS_APPEND|(bin?O_BINARY:O_TEXT));
-}
-
-/** Like write_str_to_file(), but also return -1 if there was a file
- already residing in <b>fname</b>. */
-int
-write_bytes_to_new_file(const char *fname, const char *str, size_t len,
- int bin)
-{
- return write_bytes_to_file_impl(fname, str, len,
- OPEN_FLAGS_DONT_REPLACE|
- (bin?O_BINARY:O_TEXT));
-}
-
-/**
- * Read the contents of the open file <b>fd</b> presuming it is a FIFO
- * (or similar) file descriptor for which the size of the file isn't
- * known ahead of time. Return NULL on failure, and a NUL-terminated
- * string on success. On success, set <b>sz_out</b> to the number of
- * bytes read.
- */
-char *
-read_file_to_str_until_eof(int fd, size_t max_bytes_to_read, size_t *sz_out)
-{
- ssize_t r;
- size_t pos = 0;
- char *string = NULL;
- size_t string_max = 0;
-
- if (max_bytes_to_read+1 >= SIZE_T_CEILING) {
- errno = EINVAL;
- return NULL;
- }
-
- do {
- /* XXXX This "add 1K" approach is a little goofy; if we care about
- * performance here, we should be doubling. But in practice we shouldn't
- * be using this function on big files anyway. */
- string_max = pos + 1024;
- if (string_max > max_bytes_to_read)
- string_max = max_bytes_to_read + 1;
- string = tor_realloc(string, string_max);
- r = read(fd, string + pos, string_max - pos - 1);
- if (r < 0) {
- int save_errno = errno;
- tor_free(string);
- errno = save_errno;
- return NULL;
- }
-
- pos += r;
- } while (r > 0 && pos < max_bytes_to_read);
-
- tor_assert(pos < string_max);
- *sz_out = pos;
- string[pos] = '\0';
- return string;
-}
-
-/** Read the contents of <b>filename</b> into a newly allocated
- * string; return the string on success or NULL on failure.
- *
- * If <b>stat_out</b> is provided, store the result of stat()ing the
- * file into <b>stat_out</b>.
- *
- * If <b>flags</b> & RFTS_BIN, open the file in binary mode.
- * If <b>flags</b> & RFTS_IGNORE_MISSING, don't warn if the file
- * doesn't exist.
- */
-/*
- * This function <em>may</em> return an erroneous result if the file
- * is modified while it is running, but must not crash or overflow.
- * Right now, the error case occurs when the file length grows between
- * the call to stat and the call to read_all: the resulting string will
- * be truncated.
- */
-MOCK_IMPL(char *,
-read_file_to_str, (const char *filename, int flags, struct stat *stat_out))
-{
- int fd; /* router file */
- struct stat statbuf;
- char *string;
- ssize_t r;
- int bin = flags & RFTS_BIN;
-
- tor_assert(filename);
-
- fd = tor_open_cloexec(filename,O_RDONLY|(bin?O_BINARY:O_TEXT),0);
- if (fd<0) {
- int severity = LOG_WARN;
- int save_errno = errno;
- if (errno == ENOENT && (flags & RFTS_IGNORE_MISSING))
- severity = LOG_INFO;
- log_fn(severity, LD_FS,"Could not open \"%s\": %s",filename,
- strerror(errno));
- errno = save_errno;
- return NULL;
- }
-
- if (fstat(fd, &statbuf)<0) {
- int save_errno = errno;
- close(fd);
- log_warn(LD_FS,"Could not fstat \"%s\".",filename);
- errno = save_errno;
- return NULL;
- }
-
-#ifndef _WIN32
-/** When we detect that we're reading from a FIFO, don't read more than
- * this many bytes. It's insane overkill for most uses. */
-#define FIFO_READ_MAX (1024*1024)
- if (S_ISFIFO(statbuf.st_mode)) {
- size_t sz = 0;
- string = read_file_to_str_until_eof(fd, FIFO_READ_MAX, &sz);
- int save_errno = errno;
- if (string && stat_out) {
- statbuf.st_size = sz;
- memcpy(stat_out, &statbuf, sizeof(struct stat));
- }
- close(fd);
- if (!string)
- errno = save_errno;
- return string;
- }
-#endif /* !defined(_WIN32) */
-
- if ((uint64_t)(statbuf.st_size)+1 >= SIZE_T_CEILING) {
- close(fd);
- errno = EINVAL;
- return NULL;
- }
-
- string = tor_malloc((size_t)(statbuf.st_size+1));
-
- r = read_all(fd,string,(size_t)statbuf.st_size,0);
- if (r<0) {
- int save_errno = errno;
- log_warn(LD_FS,"Error reading from file \"%s\": %s", filename,
- strerror(errno));
- tor_free(string);
- close(fd);
- errno = save_errno;
- return NULL;
- }
- string[r] = '\0'; /* NUL-terminate the result. */
-
-#if defined(_WIN32) || defined(__CYGWIN__)
- if (!bin && strchr(string, '\r')) {
- log_debug(LD_FS, "We didn't convert CRLF to LF as well as we hoped "
- "when reading %s. Coping.",
- filename);
- tor_strstrip(string, "\r");
- r = strlen(string);
- }
- if (!bin) {
- statbuf.st_size = (size_t) r;
- } else
-#endif /* defined(_WIN32) || defined(__CYGWIN__) */
- if (r != statbuf.st_size) {
- /* Unless we're using text mode on win32, we'd better have an exact
- * match for size. */
- int save_errno = errno;
- log_warn(LD_FS,"Could read only %d of %ld bytes of file \"%s\".",
- (int)r, (long)statbuf.st_size,filename);
- tor_free(string);
- close(fd);
- errno = save_errno;
- return NULL;
- }
- close(fd);
- if (stat_out) {
- memcpy(stat_out, &statbuf, sizeof(struct stat));
- }
-
- return string;
-}
-
#define TOR_ISODIGIT(c) ('0' <= (c) && (c) <= '7')
/** Given a c-style double-quoted escaped string in <b>s</b>, extract and
@@ -2040,185 +1199,6 @@ unescape_string(const char *s, char **result, size_t *size_out)
}
}
-/** Removes enclosing quotes from <b>path</b> and unescapes quotes between the
- * enclosing quotes. Backslashes are not unescaped. Return the unquoted
- * <b>path</b> on success or 0 if <b>path</b> is not quoted correctly. */
-char *
-get_unquoted_path(const char *path)
-{
- size_t len = strlen(path);
-
- if (len == 0) {
- return tor_strdup("");
- }
-
- int has_start_quote = (path[0] == '\"');
- int has_end_quote = (len > 0 && path[len-1] == '\"');
- if (has_start_quote != has_end_quote || (len == 1 && has_start_quote)) {
- return NULL;
- }
-
- char *unquoted_path = tor_malloc(len - has_start_quote - has_end_quote + 1);
- char *s = unquoted_path;
- size_t i;
- for (i = has_start_quote; i < len - has_end_quote; i++) {
- if (path[i] == '\"' && (i > 0 && path[i-1] == '\\')) {
- *(s-1) = path[i];
- } else if (path[i] != '\"') {
- *s++ = path[i];
- } else { /* unescaped quote */
- tor_free(unquoted_path);
- return NULL;
- }
- }
- *s = '\0';
- return unquoted_path;
-}
-
-/** Expand any homedir prefix on <b>filename</b>; return a newly allocated
- * string. */
-char *
-expand_filename(const char *filename)
-{
- tor_assert(filename);
-#ifdef _WIN32
- /* Might consider using GetFullPathName() as described here:
- * http://etutorials.org/Programming/secure+programming/
- * Chapter+3.+Input+Validation/3.7+Validating+Filenames+and+Paths/
- */
- return tor_strdup(filename);
-#else /* !(defined(_WIN32)) */
- if (*filename == '~') {
- char *home, *result=NULL;
- const char *rest;
-
- if (filename[1] == '/' || filename[1] == '\0') {
- home = getenv("HOME");
- if (!home) {
- log_warn(LD_CONFIG, "Couldn't find $HOME environment variable while "
- "expanding \"%s\"; defaulting to \"\".", filename);
- home = tor_strdup("");
- } else {
- home = tor_strdup(home);
- }
- rest = strlen(filename)>=2?(filename+2):"";
- } else {
-#ifdef HAVE_PWD_H
- char *username, *slash;
- slash = strchr(filename, '/');
- if (slash)
- username = tor_strndup(filename+1,slash-filename-1);
- else
- username = tor_strdup(filename+1);
- if (!(home = get_user_homedir(username))) {
- log_warn(LD_CONFIG,"Couldn't get homedir for \"%s\"",username);
- tor_free(username);
- return NULL;
- }
- tor_free(username);
- rest = slash ? (slash+1) : "";
-#else /* !(defined(HAVE_PWD_H)) */
- log_warn(LD_CONFIG, "Couldn't expand homedir on system without pwd.h");
- return tor_strdup(filename);
-#endif /* defined(HAVE_PWD_H) */
- }
- tor_assert(home);
- /* Remove trailing slash. */
- if (strlen(home)>1 && !strcmpend(home,PATH_SEPARATOR)) {
- home[strlen(home)-1] = '\0';
- }
- tor_asprintf(&result,"%s"PATH_SEPARATOR"%s",home,rest);
- tor_free(home);
- return result;
- } else {
- return tor_strdup(filename);
- }
-#endif /* defined(_WIN32) */
-}
-
-/** Return a new list containing the filenames in the directory <b>dirname</b>.
- * Return NULL on error or if <b>dirname</b> is not a directory.
- */
-MOCK_IMPL(smartlist_t *,
-tor_listdir, (const char *dirname))
-{
- smartlist_t *result;
-#ifdef _WIN32
- char *pattern=NULL;
- TCHAR tpattern[MAX_PATH] = {0};
- char name[MAX_PATH*2+1] = {0};
- HANDLE handle;
- WIN32_FIND_DATA findData;
- tor_asprintf(&pattern, "%s\\*", dirname);
-#ifdef UNICODE
- mbstowcs(tpattern,pattern,MAX_PATH);
-#else
- strlcpy(tpattern, pattern, MAX_PATH);
-#endif
- if (INVALID_HANDLE_VALUE == (handle = FindFirstFile(tpattern, &findData))) {
- tor_free(pattern);
- return NULL;
- }
- result = smartlist_new();
- while (1) {
-#ifdef UNICODE
- wcstombs(name,findData.cFileName,MAX_PATH);
- name[sizeof(name)-1] = '\0';
-#else
- strlcpy(name,findData.cFileName,sizeof(name));
-#endif /* defined(UNICODE) */
- if (strcmp(name, ".") &&
- strcmp(name, "..")) {
- smartlist_add_strdup(result, name);
- }
- if (!FindNextFile(handle, &findData)) {
- DWORD err;
- if ((err = GetLastError()) != ERROR_NO_MORE_FILES) {
- char *errstr = format_win32_error(err);
- log_warn(LD_FS, "Error reading directory '%s': %s", dirname, errstr);
- tor_free(errstr);
- }
- break;
- }
- }
- FindClose(handle);
- tor_free(pattern);
-#else /* !(defined(_WIN32)) */
- const char *prot_dname = sandbox_intern_string(dirname);
- DIR *d;
- struct dirent *de;
- if (!(d = opendir(prot_dname)))
- return NULL;
-
- result = smartlist_new();
- while ((de = readdir(d))) {
- if (!strcmp(de->d_name, ".") ||
- !strcmp(de->d_name, ".."))
- continue;
- smartlist_add_strdup(result, de->d_name);
- }
- closedir(d);
-#endif /* defined(_WIN32) */
- return result;
-}
-
-/** Return true iff <b>filename</b> is a relative path. */
-int
-path_is_relative(const char *filename)
-{
- if (filename && filename[0] == '/')
- return 0;
-#ifdef _WIN32
- else if (filename && filename[0] == '\\')
- return 0;
- else if (filename && strlen(filename)>3 && TOR_ISALPHA(filename[0]) &&
- filename[1] == ':' && filename[2] == '\\')
- return 0;
-#endif /* defined(_WIN32) */
- else
- return 1;
-}
-
/* =====
* Process helpers
* ===== */
diff --git a/src/common/util.h b/src/common/util.h
index b7ac2a176..4f8d6395d 100644
--- a/src/common/util.h
+++ b/src/common/util.h
@@ -35,16 +35,9 @@
#include "lib/log/ratelim.h"
#include "lib/log/util_bug.h"
#include "lib/log/escape.h"
-
-#ifndef O_BINARY
-#define O_BINARY 0
-#endif
-#ifndef O_TEXT
-#define O_TEXT 0
-#endif
-#ifndef O_NOFOLLOW
-#define O_NOFOLLOW 0
-#endif
+#include "lib/fs/dir.h"
+#include "lib/fs/files.h"
+#include "lib/fs/path.h"
uint64_t tor_htonll(uint64_t a);
uint64_t tor_ntohll(uint64_t a);
@@ -117,8 +110,6 @@ int parse_http_time(const char *buf, struct tm *tm);
int format_time_interval(char *out, size_t out_len, long interval);
/* File helpers */
-ssize_t write_all_to_fd(int fd, const char *buf, size_t count);
-ssize_t read_all_from_fd(int fd, char *buf, size_t count);
#define write_all(fd, buf, count, isSock) \
((isSock) ? write_all_to_socket((fd), (buf), (count)) \
@@ -139,76 +130,7 @@ const char *stream_status_to_string(enum stream_status stream_status);
enum stream_status get_string_from_pipe(int fd, char *buf, size_t count);
-MOCK_DECL(int,tor_unlink,(const char *pathname));
-
-/** Return values from file_status(); see that function's documentation
- * for details. */
-typedef enum { FN_ERROR, FN_NOENT, FN_FILE, FN_DIR, FN_EMPTY } file_status_t;
-file_status_t file_status(const char *filename);
-
-/** Possible behaviors for check_private_dir() on encountering a nonexistent
- * directory; see that function's documentation for details. */
-typedef unsigned int cpd_check_t;
-#define CPD_NONE 0
-#define CPD_CREATE (1u << 0)
-#define CPD_CHECK (1u << 1)
-#define CPD_GROUP_OK (1u << 2)
-#define CPD_GROUP_READ (1u << 3)
-#define CPD_CHECK_MODE_ONLY (1u << 4)
-#define CPD_RELAX_DIRMODE_CHECK (1u << 5)
-MOCK_DECL(int, check_private_dir,
- (const char *dirname, cpd_check_t check,
- const char *effective_user));
-
-#define OPEN_FLAGS_REPLACE (O_WRONLY|O_CREAT|O_TRUNC)
-#define OPEN_FLAGS_APPEND (O_WRONLY|O_CREAT|O_APPEND)
-#define OPEN_FLAGS_DONT_REPLACE (O_CREAT|O_EXCL|O_APPEND|O_WRONLY)
-typedef struct open_file_t open_file_t;
-int start_writing_to_file(const char *fname, int open_flags, int mode,
- open_file_t **data_out);
-FILE *start_writing_to_stdio_file(const char *fname, int open_flags, int mode,
- open_file_t **data_out);
-FILE *fdopen_file(open_file_t *file_data);
-int finish_writing_to_file(open_file_t *file_data);
-int abort_writing_to_file(open_file_t *file_data);
-MOCK_DECL(int,
-write_str_to_file,(const char *fname, const char *str, int bin));
-MOCK_DECL(int,
-write_bytes_to_file,(const char *fname, const char *str, size_t len,
- int bin));
-/** An ad-hoc type to hold a string of characters and a count; used by
- * write_chunks_to_file. */
-typedef struct sized_chunk_t {
- const char *bytes;
- size_t len;
-} sized_chunk_t;
-struct smartlist_t;
-int write_chunks_to_file(const char *fname, const struct smartlist_t *chunks,
- int bin, int no_tempfile);
-int append_bytes_to_file(const char *fname, const char *str, size_t len,
- int bin);
-int write_bytes_to_new_file(const char *fname, const char *str, size_t len,
- int bin);
-
-/** Flag for read_file_to_str: open the file in binary mode. */
-#define RFTS_BIN 1
-/** Flag for read_file_to_str: it's okay if the file doesn't exist. */
-#define RFTS_IGNORE_MISSING 2
-
-#ifndef _WIN32
-struct stat;
-#endif
-MOCK_DECL_ATTR(char *, read_file_to_str,
- (const char *filename, int flags, struct stat *stat_out),
- ATTR_MALLOC);
-char *read_file_to_str_until_eof(int fd, size_t max_bytes_to_read,
- size_t *sz_out)
- ATTR_MALLOC;
const char *unescape_string(const char *s, char **result, size_t *size_out);
-char *get_unquoted_path(const char *path);
-char *expand_filename(const char *filename);
-MOCK_DECL(struct smartlist_t *, tor_listdir, (const char *dirname));
-int path_is_relative(const char *filename);
/* Process helpers */
void start_daemon(void);
diff --git a/src/include.am b/src/include.am
index 5d28ea34e..7818fb4f6 100644
--- a/src/include.am
+++ b/src/include.am
@@ -7,6 +7,7 @@ include src/lib/container/include.am
include src/lib/crypt_ops/include.am
include src/lib/defs/include.am
include src/lib/fdio/include.am
+include src/lib/fs/include.am
include src/lib/include.libdonna.am
include src/lib/intmath/include.am
include src/lib/lock/include.am
diff --git a/src/lib/fs/.may_include b/src/lib/fs/.may_include
new file mode 100644
index 000000000..2321edbbe
--- /dev/null
+++ b/src/lib/fs/.may_include
@@ -0,0 +1,11 @@
+orconfig.h
+lib/cc/*.h
+lib/container/*.h
+lib/err/*.h
+lib/fdio/*.h
+lib/malloc/*.h
+lib/fs/*.h
+lib/sandbox/*.h
+lib/string/*.h
+lib/testsupport/testsupport.h
+lib/log/*.h
diff --git a/src/lib/fs/dir.c b/src/lib/fs/dir.c
new file mode 100644
index 000000000..9f09327d8
--- /dev/null
+++ b/src/lib/fs/dir.c
@@ -0,0 +1,360 @@
+/* Copyright (c) 2003, Roger Dingledine
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "lib/fs/dir.h"
+#include "lib/fs/path.h"
+#include "lib/fs/userdb.h"
+
+#include "lib/log/torlog.h"
+#include "lib/log/util_bug.h"
+#include "lib/log/win32err.h"
+#include "lib/container/smartlist.h"
+#include "lib/sandbox/sandbox.h"
+#include "lib/malloc/util_malloc.h"
+#include "lib/string/printf.h"
+#include "lib/string/compat_string.h"
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#ifdef _WIN32
+#include <io.h>
+#include <direct.h>
+#include <windows.h>
+#else /* !(defined(_WIN32)) */
+#include <dirent.h>
+#include <pwd.h>
+#include <grp.h>
+#endif /* defined(_WIN32) */
+
+#include <errno.h>
+#include <string.h>
+
+/** Check whether <b>dirname</b> exists and is private. If yes return 0.
+ * If <b>dirname</b> does not exist:
+ * - if <b>check</b>&CPD_CREATE, try to create it and return 0 on success.
+ * - if <b>check</b>&CPD_CHECK, and we think we can create it, return 0.
+ * - if <b>check</b>&CPD_CHECK is false, and the directory exists, return 0.
+ * - otherwise, return -1.
+ * If CPD_GROUP_OK is set, then it's okay if the directory
+ * is group-readable, but in all cases we create the directory mode 0700.
+ * If CPD_GROUP_READ is set, existing directory behaves as CPD_GROUP_OK and
+ * if the directory is created it will use mode 0750 with group read
+ * permission. Group read privileges also assume execute permission
+ * as norm for directories. If CPD_CHECK_MODE_ONLY is set, then we don't
+ * alter the directory permissions if they are too permissive:
+ * we just return -1.
+ * When effective_user is not NULL, check permissions against the given user
+ * and its primary group.
+ */
+MOCK_IMPL(int,
+check_private_dir,(const char *dirname, cpd_check_t check,
+ const char *effective_user))
+{
+ int r;
+ struct stat st;
+
+ tor_assert(dirname);
+
+#ifndef _WIN32
+ int fd;
+ const struct passwd *pw = NULL;
+ uid_t running_uid;
+ gid_t running_gid;
+
+ /*
+ * Goal is to harden the implementation by removing any
+ * potential for race between stat() and chmod().
+ * chmod() accepts filename as argument. If an attacker can move
+ * the file between stat() and chmod(), a potential race exists.
+ *
+ * Several suggestions taken from:
+ * https://developer.apple.com/library/mac/documentation/
+ * Security/Conceptual/SecureCodingGuide/Articles/RaceConditions.html
+ */
+
+ /* Open directory.
+ * O_NOFOLLOW to ensure that it does not follow symbolic links */
+ fd = open(sandbox_intern_string(dirname), O_NOFOLLOW);
+
+ /* Was there an error? Maybe the directory does not exist? */
+ if (fd == -1) {
+
+ if (errno != ENOENT) {
+ /* Other directory error */
+ log_warn(LD_FS, "Directory %s cannot be read: %s", dirname,
+ strerror(errno));
+ return -1;
+ }
+
+ /* Received ENOENT: Directory does not exist */
+
+ /* Should we create the directory? */
+ if (check & CPD_CREATE) {
+ log_info(LD_GENERAL, "Creating directory %s", dirname);
+ if (check & CPD_GROUP_READ) {
+ r = mkdir(dirname, 0750);
+ } else {
+ r = mkdir(dirname, 0700);
+ }
+
+ /* check for mkdir() error */
+ if (r) {
+ log_warn(LD_FS, "Error creating directory %s: %s", dirname,
+ strerror(errno));
+ return -1;
+ }
+
+ /* we just created the directory. try to open it again.
+ * permissions on the directory will be checked again below.*/
+ fd = open(sandbox_intern_string(dirname), O_NOFOLLOW);
+
+ if (fd == -1) {
+ log_warn(LD_FS, "Could not reopen recently created directory %s: %s",
+ dirname,
+ strerror(errno));
+ return -1;
+ } else {
+ close(fd);
+ }
+
+ } else if (!(check & CPD_CHECK)) {
+ log_warn(LD_FS, "Directory %s does not exist.", dirname);
+ return -1;
+ }
+
+ /* XXXX In the case where check==CPD_CHECK, we should look at the
+ * parent directory a little harder. */
+ return 0;
+ }
+
+ tor_assert(fd >= 0);
+
+ //f = tor_strdup(dirname);
+ //clean_name_for_stat(f);
+ log_debug(LD_FS, "stat()ing %s", dirname);
+ //r = stat(sandbox_intern_string(f), &st);
+ r = fstat(fd, &st);
+ if (r == -1) {
+ log_warn(LD_FS, "fstat() on directory %s failed.", dirname);
+ close(fd);
+ return -1;
+ }
+ //tor_free(f);
+
+ /* check that dirname is a directory */
+ if (!(st.st_mode & S_IFDIR)) {
+ log_warn(LD_FS, "%s is not a directory", dirname);
+ close(fd);
+ return -1;
+ }
+
+ if (effective_user) {
+ /* Look up the user and group information.
+ * If we have a problem, bail out. */
+ pw = tor_getpwnam(effective_user);
+ if (pw == NULL) {
+ log_warn(LD_CONFIG, "Error setting configured user: %s not found",
+ effective_user);
+ close(fd);
+ return -1;
+ }
+ running_uid = pw->pw_uid;
+ running_gid = pw->pw_gid;
+ } else {
+ running_uid = getuid();
+ running_gid = getgid();
+ }
+ if (st.st_uid != running_uid) {
+ char *process_ownername = NULL, *file_ownername = NULL;
+
+ {
+ const struct passwd *pw_running = tor_getpwuid(running_uid);
+ process_ownername = pw_running ? tor_strdup(pw_running->pw_name) :
+ tor_strdup("<unknown>");
+ }
+
+ {
+ const struct passwd *pw_stat = tor_getpwuid(st.st_uid);
+ file_ownername = pw_stat ? tor_strdup(pw_stat->pw_name) :
+ tor_strdup("<unknown>");
+ }
+
+ log_warn(LD_FS, "%s is not owned by this user (%s, %d) but by "
+ "%s (%d). Perhaps you are running Tor as the wrong user?",
+ dirname, process_ownername, (int)running_uid,
+ file_ownername, (int)st.st_uid);
+
+ tor_free(process_ownername);
+ tor_free(file_ownername);
+ close(fd);
+ return -1;
+ }
+ if ( (check & (CPD_GROUP_OK|CPD_GROUP_READ))
+ && (st.st_gid != running_gid) && (st.st_gid != 0)) {
+ struct group *gr;
+ char *process_groupname = NULL;
+ gr = getgrgid(running_gid);
+ process_groupname = gr ? tor_strdup(gr->gr_name) : tor_strdup("<unknown>");
+ gr = getgrgid(st.st_gid);
+
+ log_warn(LD_FS, "%s is not owned by this group (%s, %d) but by group "
+ "%s (%d). Are you running Tor as the wrong user?",
+ dirname, process_groupname, (int)running_gid,
+ gr ? gr->gr_name : "<unknown>", (int)st.st_gid);
+
+ tor_free(process_groupname);
+ close(fd);
+ return -1;
+ }
+ unsigned unwanted_bits = 0;
+ if (check & (CPD_GROUP_OK|CPD_GROUP_READ)) {
+ unwanted_bits = 0027;
+ } else {
+ unwanted_bits = 0077;
+ }
+ unsigned check_bits_filter = ~0;
+ if (check & CPD_RELAX_DIRMODE_CHECK) {
+ check_bits_filter = 0022;
+ }
+ if ((st.st_mode & unwanted_bits & check_bits_filter) != 0) {
+ unsigned new_mode;
+ if (check & CPD_CHECK_MODE_ONLY) {
+ log_warn(LD_FS, "Permissions on directory %s are too permissive.",
+ dirname);
+ close(fd);
+ return -1;
+ }
+ log_warn(LD_FS, "Fixing permissions on directory %s", dirname);
+ new_mode = st.st_mode;
+ new_mode |= 0700; /* Owner should have rwx */
+ if (check & CPD_GROUP_READ) {
+ new_mode |= 0050; /* Group should have rx */
+ }
+ new_mode &= ~unwanted_bits; /* Clear the bits that we didn't want set...*/
+ if (fchmod(fd, new_mode)) {
+ log_warn(LD_FS, "Could not chmod directory %s: %s", dirname,
+ strerror(errno));
+ close(fd);
+ return -1;
+ } else {
+ close(fd);
+ return 0;
+ }
+ }
+ close(fd);
+#else /* !(!defined(_WIN32)) */
+ /* Win32 case: we can't open() a directory. */
+ (void)effective_user;
+
+ char *f = tor_strdup(dirname);
+ clean_fname_for_stat(f);
+ log_debug(LD_FS, "stat()ing %s", f);
+ r = stat(sandbox_intern_string(f), &st);
+ tor_free(f);
+ if (r) {
+ if (errno != ENOENT) {
+ log_warn(LD_FS, "Directory %s cannot be read: %s", dirname,
+ strerror(errno));
+ return -1;
+ }
+ if (check & CPD_CREATE) {
+ log_info(LD_GENERAL, "Creating directory %s", dirname);
+ r = mkdir(dirname);
+ if (r) {
+ log_warn(LD_FS, "Error creating directory %s: %s", dirname,
+ strerror(errno));
+ return -1;
+ }
+ } else if (!(check & CPD_CHECK)) {
+ log_warn(LD_FS, "Directory %s does not exist.", dirname);
+ return -1;
+ }
+ return 0;
+ }
+ if (!(st.st_mode & S_IFDIR)) {
+ log_warn(LD_FS, "%s is not a directory", dirname);
+ return -1;
+ }
+
+#endif /* !defined(_WIN32) */
+ return 0;
+}
+
+/** Return a new list containing the filenames in the directory <b>dirname</b>.
+ * Return NULL on error or if <b>dirname</b> is not a directory.
+ */
+MOCK_IMPL(smartlist_t *,
+tor_listdir, (const char *dirname))
+{
+ smartlist_t *result;
+#ifdef _WIN32
+ char *pattern=NULL;
+ TCHAR tpattern[MAX_PATH] = {0};
+ char name[MAX_PATH*2+1] = {0};
+ HANDLE handle;
+ WIN32_FIND_DATA findData;
+ tor_asprintf(&pattern, "%s\\*", dirname);
+#ifdef UNICODE
+ mbstowcs(tpattern,pattern,MAX_PATH);
+#else
+ strlcpy(tpattern, pattern, MAX_PATH);
+#endif
+ if (INVALID_HANDLE_VALUE == (handle = FindFirstFile(tpattern, &findData))) {
+ tor_free(pattern);
+ return NULL;
+ }
+ result = smartlist_new();
+ while (1) {
+#ifdef UNICODE
+ wcstombs(name,findData.cFileName,MAX_PATH);
+ name[sizeof(name)-1] = '\0';
+#else
+ strlcpy(name,findData.cFileName,sizeof(name));
+#endif /* defined(UNICODE) */
+ if (strcmp(name, ".") &&
+ strcmp(name, "..")) {
+ smartlist_add_strdup(result, name);
+ }
+ if (!FindNextFile(handle, &findData)) {
+ DWORD err;
+ if ((err = GetLastError()) != ERROR_NO_MORE_FILES) {
+ char *errstr = format_win32_error(err);
+ log_warn(LD_FS, "Error reading directory '%s': %s", dirname, errstr);
+ tor_free(errstr);
+ }
+ break;
+ }
+ }
+ FindClose(handle);
+ tor_free(pattern);
+#else /* !(defined(_WIN32)) */
+ const char *prot_dname = sandbox_intern_string(dirname);
+ DIR *d;
+ struct dirent *de;
+ if (!(d = opendir(prot_dname)))
+ return NULL;
+
+ result = smartlist_new();
+ while ((de = readdir(d))) {
+ if (!strcmp(de->d_name, ".") ||
+ !strcmp(de->d_name, ".."))
+ continue;
+ smartlist_add_strdup(result, de->d_name);
+ }
+ closedir(d);
+#endif /* defined(_WIN32) */
+ return result;
+}
diff --git a/src/lib/fs/dir.h b/src/lib/fs/dir.h
new file mode 100644
index 000000000..925211fbd
--- /dev/null
+++ b/src/lib/fs/dir.h
@@ -0,0 +1,27 @@
+/* Copyright (c) 2003-2004, Roger Dingledine
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#ifndef TOR_DIR_H
+#define TOR_DIR_H
+
+#include "lib/cc/compat_compiler.h"
+#include "lib/testsupport/testsupport.h"
+
+/** Possible behaviors for check_private_dir() on encountering a nonexistent
+ * directory; see that function's documentation for details. */
+typedef unsigned int cpd_check_t;
+#define CPD_NONE 0
+#define CPD_CREATE (1u << 0)
+#define CPD_CHECK (1u << 1)
+#define CPD_GROUP_OK (1u << 2)
+#define CPD_GROUP_READ (1u << 3)
+#define CPD_CHECK_MODE_ONLY (1u << 4)
+#define CPD_RELAX_DIRMODE_CHECK (1u << 5)
+MOCK_DECL(int, check_private_dir, (const char *dirname, cpd_check_t check,
+ const char *effective_user));
+
+MOCK_DECL(struct smartlist_t *, tor_listdir, (const char *dirname));
+
+#endif
diff --git a/src/lib/fs/files.c b/src/lib/fs/files.c
new file mode 100644
index 000000000..0335f6dc5
--- /dev/null
+++ b/src/lib/fs/files.c
@@ -0,0 +1,711 @@
+/* Copyright (c) 2003-2004, Roger Dingledine
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#ifdef _WIN32
+#include <windows.h>
+#endif
+
+#include "lib/fs/files.h"
+#include "lib/fs/path.h"
+#include "lib/container/smartlist.h"
+#include "lib/log/torlog.h"
+#include "lib/log/util_bug.h"
+#include "lib/log/escape.h"
+#include "lib/err/torerr.h"
+#include "lib/malloc/util_malloc.h"
+#include "lib/sandbox/sandbox.h"
+#include "lib/string/printf.h"
+#include "lib/string/util_string.h"
+#include "lib/fdio/fdio.h"
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_UTIME_H
+#include <utime.h>
+#endif
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+/** As open(path, flags, mode), but return an fd with the close-on-exec mode
+ * set. */
+int
+tor_open_cloexec(const char *path, int flags, unsigned mode)
+{
+ int fd;
+ const char *p = sandbox_intern_string(path);
+#ifdef O_CLOEXEC
+ fd = open(p, flags|O_CLOEXEC, mode);
+ if (fd >= 0)
+ return fd;
+ /* If we got an error, see if it is EINVAL. EINVAL might indicate that,
+ * even though we were built on a system with O_CLOEXEC support, we
+ * are running on one without. */
+ if (errno != EINVAL)
+ return -1;
+#endif /* defined(O_CLOEXEC) */
+
+ log_debug(LD_FS, "Opening %s with flags %x", p, flags);
+ fd = open(p, flags, mode);
+#ifdef FD_CLOEXEC
+ if (fd >= 0) {
+ if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) {
+ log_warn(LD_FS,"Couldn't set FD_CLOEXEC: %s", strerror(errno));
+ close(fd);
+ return -1;
+ }
+ }
+#endif /* defined(FD_CLOEXEC) */
+ return fd;
+}
+
+/** As fopen(path,mode), but ensures that the O_CLOEXEC bit is set on the
+ * underlying file handle. */
+FILE *
+tor_fopen_cloexec(const char *path, const char *mode)
+{
+ FILE *result = fopen(path, mode);
+#ifdef FD_CLOEXEC
+ if (result != NULL) {
+ if (fcntl(fileno(result), F_SETFD, FD_CLOEXEC) == -1) {
+ log_warn(LD_FS,"Couldn't set FD_CLOEXEC: %s", strerror(errno));
+ fclose(result);
+ return NULL;
+ }
+ }
+#endif /* defined(FD_CLOEXEC) */
+ return result;
+}
+
+/** As rename(), but work correctly with the sandbox. */
+int
+tor_rename(const char *path_old, const char *path_new)
+{
+ log_debug(LD_FS, "Renaming %s to %s", path_old, path_new);
+ return rename(sandbox_intern_string(path_old),
+ sandbox_intern_string(path_new));
+}
+
+/**
+ * Rename the file <b>from</b> to the file <b>to</b>. On Unix, this is
+ * the same as rename(2). On windows, this removes <b>to</b> first if
+ * it already exists.
+ * Returns 0 on success. Returns -1 and sets errno on failure.
+ */
+int
+replace_file(const char *from, const char *to)
+{
+#ifndef _WIN32
+ return tor_rename(from, to);
+#else
+ switch (file_status(to))
+ {
+ case FN_NOENT:
+ break;
+ case FN_FILE:
+ case FN_EMPTY:
+ if (unlink(to)) return -1;
+ break;
+ case FN_ERROR:
+ return -1;
+ case FN_DIR:
+ errno = EISDIR;
+ return -1;
+ }
+ return tor_rename(from,to);
+#endif /* !defined(_WIN32) */
+}
+
+/** Change <b>fname</b>'s modification time to now. */
+int
+touch_file(const char *fname)
+{
+ if (utime(fname, NULL)!=0)
+ return -1;
+ return 0;
+}
+
+/** Wrapper for unlink() to make it mockable for the test suite; returns 0
+ * if unlinking the file succeeded, -1 and sets errno if unlinking fails.
+ */
+
+MOCK_IMPL(int,
+tor_unlink,(const char *pathname))
+{
+ return unlink(pathname);
+}
+
+/** Write <b>count</b> bytes from <b>buf</b> to <b>fd</b>. Return the number
+ * of bytes written, or -1 on error. Only use if fd is a blocking fd. */
+ssize_t
+write_all_to_fd(int fd, const char *buf, size_t count)
+{
+ size_t written = 0;
+ ssize_t result;
+ raw_assert(count < SSIZE_MAX);
+
+ while (written != count) {
+ result = write(fd, buf+written, count-written);
+ if (result<0)
+ return -1;
+ written += result;
+ }
+ return (ssize_t)count;
+}
+
+/** Read from <b>fd</b> to <b>buf</b>, until we get <b>count</b> bytes or
+ * reach the end of the file. Return the number of bytes read, or -1 on
+ * error. Only use if fd is a blocking fd. */
+ssize_t
+read_all_from_fd(int fd, char *buf, size_t count)
+{
+ size_t numread = 0;
+ ssize_t result;
+
+ if (count > SIZE_T_CEILING || count > SSIZE_MAX) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ while (numread < count) {
+ result = read(fd, buf+numread, count-numread);
+ if (result<0)
+ return -1;
+ else if (result == 0)
+ break;
+ numread += result;
+ }
+ return (ssize_t)numread;
+}
+
+/** Return:
+ * FN_ERROR if filename can't be read, is NULL, or is zero-length,
+ * FN_NOENT if it doesn't exist,
+ * FN_FILE if it is a non-empty regular file, or a FIFO on unix-like systems,
+ * FN_EMPTY for zero-byte regular files,
+ * FN_DIR if it's a directory, and
+ * FN_ERROR for any other file type.
+ * On FN_ERROR and FN_NOENT, sets errno. (errno is not set when FN_ERROR
+ * is returned due to an unhandled file type.) */
+file_status_t
+file_status(const char *fname)
+{
+ struct stat st;
+ char *f;
+ int r;
+ if (!fname || strlen(fname) == 0) {
+ return FN_ERROR;
+ }
+ f = tor_strdup(fname);
+ clean_fname_for_stat(f);
+ log_debug(LD_FS, "stat()ing %s", f);
+ r = stat(sandbox_intern_string(f), &st);
+ tor_free(f);
+ if (r) {
+ if (errno == ENOENT) {
+ return FN_NOENT;
+ }
+ return FN_ERROR;
+ }
+ if (st.st_mode & S_IFDIR) {
+ return FN_DIR;
+ } else if (st.st_mode & S_IFREG) {
+ if (st.st_size > 0) {
+ return FN_FILE;
+ } else if (st.st_size == 0) {
+ return FN_EMPTY;
+ } else {
+ return FN_ERROR;
+ }
+#ifndef _WIN32
+ } else if (st.st_mode & S_IFIFO) {
+ return FN_FILE;
+#endif
+ } else {
+ return FN_ERROR;
+ }
+}
+
+/** Create a file named <b>fname</b> with the contents <b>str</b>. Overwrite
+ * the previous <b>fname</b> if possible. Return 0 on success, -1 on failure.
+ *
+ * This function replaces the old file atomically, if possible. This
+ * function, and all other functions in util.c that create files, create them
+ * with mode 0600.
+ */
+MOCK_IMPL(int,
+write_str_to_file,(const char *fname, const char *str, int bin))
+{
+#ifdef _WIN32
+ if (!bin && strchr(str, '\r')) {
+ log_warn(LD_BUG,
+ "We're writing a text string that already contains a CR to %s",
+ escaped(fname));
+ }
+#endif /* defined(_WIN32) */
+ return write_bytes_to_file(fname, str, strlen(str), bin);
+}
+
+/** Represents a file that we're writing to, with support for atomic commit:
+ * we can write into a temporary file, and either remove the file on
+ * failure, or replace the original file on success. */
+struct open_file_t {
+ char *tempname; /**< Name of the temporary file. */
+ char *filename; /**< Name of the original file. */
+ unsigned rename_on_close:1; /**< Are we using the temporary file or not? */
+ unsigned binary:1; /**< Did we open in binary mode? */
+ int fd; /**< fd for the open file. */
+ FILE *stdio_file; /**< stdio wrapper for <b>fd</b>. */
+};
+
+/** Try to start writing to the file in <b>fname</b>, passing the flags
+ * <b>open_flags</b> to the open() syscall, creating the file (if needed) with
+ * access value <b>mode</b>. If the O_APPEND flag is set, we append to the
+ * original file. Otherwise, we open a new temporary file in the same
+ * directory, and either replace the original or remove the temporary file
+ * when we're done.
+ *
+ * Return the fd for the newly opened file, and store working data in
+ * *<b>data_out</b>. The caller should not close the fd manually:
+ * instead, call finish_writing_to_file() or abort_writing_to_file().
+ * Returns -1 on failure.
+ *
+ * NOTE: When not appending, the flags O_CREAT and O_TRUNC are treated
+ * as true and the flag O_EXCL is treated as false.
+ *
+ * NOTE: Ordinarily, O_APPEND means "seek to the end of the file before each
+ * write()". We don't do that.
+ */
+int
+start_writing_to_file(const char *fname, int open_flags, int mode,
+ open_file_t **data_out)
+{
+ open_file_t *new_file = tor_malloc_zero(sizeof(open_file_t));
+ const char *open_name;
+ int append = 0;
+
+ tor_assert(fname);
+ tor_assert(data_out);
+#if (O_BINARY != 0 && O_TEXT != 0)
+ tor_assert((open_flags & (O_BINARY|O_TEXT)) != 0);
+#endif
+ new_file->fd = -1;
+ new_file->filename = tor_strdup(fname);
+ if (open_flags & O_APPEND) {
+ open_name = fname;
+ new_file->rename_on_close = 0;
+ append = 1;
+ open_flags &= ~O_APPEND;
+ } else {
+ tor_asprintf(&new_file->tempname, "%s.tmp", fname);
+ open_name = new_file->tempname;
+ /* We always replace an existing temporary file if there is one. */
+ open_flags |= O_CREAT|O_TRUNC;
+ open_flags &= ~O_EXCL;
+ new_file->rename_on_close = 1;
+ }
+#if O_BINARY != 0
+ if (open_flags & O_BINARY)
+ new_file->binary = 1;
+#endif
+
+ new_file->fd = tor_open_cloexec(open_name, open_flags, mode);
+ if (new_file->fd < 0) {
+ log_warn(LD_FS, "Couldn't open \"%s\" (%s) for writing: %s",
+ open_name, fname, strerror(errno));
+ goto err;
+ }
+ if (append) {
+ if (tor_fd_seekend(new_file->fd) < 0) {
+ log_warn(LD_FS, "Couldn't seek to end of file \"%s\": %s", open_name,
+ strerror(errno));
+ goto err;
+ }
+ }
+
+ *data_out = new_file;
+
+ return new_file->fd;
+
+ err:
+ if (new_file->fd >= 0)
+ close(new_file->fd);
+ *data_out = NULL;
+ tor_free(new_file->filename);
+ tor_free(new_file->tempname);
+ tor_free(new_file);
+ return -1;
+}
+
+/** Given <b>file_data</b> from start_writing_to_file(), return a stdio FILE*
+ * that can be used to write to the same file. The caller should not mix
+ * stdio calls with non-stdio calls. */
+FILE *
+fdopen_file(open_file_t *file_data)
+{
+ tor_assert(file_data);
+ if (file_data->stdio_file)
+ return file_data->stdio_file;
+ tor_assert(file_data->fd >= 0);
+ if (!(file_data->stdio_file = fdopen(file_data->fd,
+ file_data->binary?"ab":"a"))) {
+ log_warn(LD_FS, "Couldn't fdopen \"%s\" [%d]: %s", file_data->filename,
+ file_data->fd, strerror(errno));
+ }
+ return file_data->stdio_file;
+}
+
+/** Combines start_writing_to_file with fdopen_file(): arguments are as
+ * for start_writing_to_file, but */
+FILE *
+start_writing_to_stdio_file(const char *fname, int open_flags, int mode,
+ open_file_t **data_out)
+{
+ FILE *res;
+ if (start_writing_to_file(fname, open_flags, mode, data_out)<0)
+ return NULL;
+ if (!(res = fdopen_file(*data_out))) {
+ abort_writing_to_file(*data_out);
+ *data_out = NULL;
+ }
+ return res;
+}
+
+/** Helper function: close and free the underlying file and memory in
+ * <b>file_data</b>. If we were writing into a temporary file, then delete
+ * that file (if abort_write is true) or replaces the target file with
+ * the temporary file (if abort_write is false). */
+static int
+finish_writing_to_file_impl(open_file_t *file_data, int abort_write)
+{
+ int r = 0;
+
+ tor_assert(file_data && file_data->filename);
+ if (file_data->stdio_file) {
+ if (fclose(file_data->stdio_file)) {
+ log_warn(LD_FS, "Error closing \"%s\": %s", file_data->filename,
+ strerror(errno));
+ abort_write = r = -1;
+ }
+ } else if (file_data->fd >= 0 && close(file_data->fd) < 0) {
+ log_warn(LD_FS, "Error flushing \"%s\": %s", file_data->filename,
+ strerror(errno));
+ abort_write = r = -1;
+ }
+
+ if (file_data->rename_on_close) {
+ tor_assert(file_data->tempname && file_data->filename);
+ if (!abort_write) {
+ tor_assert(strcmp(file_data->filename, file_data->tempname));
+ if (replace_file(file_data->tempname, file_data->filename)) {
+ log_warn(LD_FS, "Error replacing \"%s\": %s", file_data->filename,
+ strerror(errno));
+ abort_write = r = -1;
+ }
+ }
+ if (abort_write) {
+ int res = unlink(file_data->tempname);
+ if (res != 0) {
+ /* We couldn't unlink and we'll leave a mess behind */
+ log_warn(LD_FS, "Failed to unlink %s: %s",
+ file_data->tempname, strerror(errno));
+ r = -1;
+ }
+ }
+ }
+
+ tor_free(file_data->filename);
+ tor_free(file_data->tempname);
+ tor_free(file_data);
+
+ return r;
+}
+
+/** Finish writing to <b>file_data</b>: close the file handle, free memory as
+ * needed, and if using a temporary file, replace the original file with
+ * the temporary file. */
+int
+finish_writing_to_file(open_file_t *file_data)
+{
+ return finish_writing_to_file_impl(file_data, 0);
+}
+
+/** Finish writing to <b>file_data</b>: close the file handle, free memory as
+ * needed, and if using a temporary file, delete it. */
+int
+abort_writing_to_file(open_file_t *file_data)
+{
+ return finish_writing_to_file_impl(file_data, 1);
+}
+
+/** Helper: given a set of flags as passed to open(2), open the file
+ * <b>fname</b> and write all the sized_chunk_t structs in <b>chunks</b> to
+ * the file. Do so as atomically as possible e.g. by opening temp files and
+ * renaming. */
+static int
+write_chunks_to_file_impl(const char *fname, const smartlist_t *chunks,
+ int open_flags)
+{
+ open_file_t *file = NULL;
+ int fd;
+ ssize_t result;
+ fd = start_writing_to_file(fname, open_flags, 0600, &file);
+ if (fd<0)
+ return -1;
+ SMARTLIST_FOREACH(chunks, sized_chunk_t *, chunk,
+ {
+ result = write_all_to_fd(fd, chunk->bytes, chunk->len);
+ if (result < 0) {
+ log_warn(LD_FS, "Error writing to \"%s\": %s", fname,
+ strerror(errno));
+ goto err;
+ }
+ tor_assert((size_t)result == chunk->len);
+ });
+
+ return finish_writing_to_file(file);
+ err:
+ abort_writing_to_file(file);
+ return -1;
+}
+
+/** Given a smartlist of sized_chunk_t, write them to a file
+ * <b>fname</b>, overwriting or creating the file as necessary.
+ * If <b>no_tempfile</b> is 0 then the file will be written
+ * atomically. */
+int
+write_chunks_to_file(const char *fname, const smartlist_t *chunks, int bin,
+ int no_tempfile)
+{
+ int flags = OPEN_FLAGS_REPLACE|(bin?O_BINARY:O_TEXT);
+
+ if (no_tempfile) {
+ /* O_APPEND stops write_chunks_to_file from using tempfiles */
+ flags |= O_APPEND;
+ }
+ return write_chunks_to_file_impl(fname, chunks, flags);
+}
+
+/** Write <b>len</b> bytes, starting at <b>str</b>, to <b>fname</b>
+ using the open() flags passed in <b>flags</b>. */
+static int
+write_bytes_to_file_impl(const char *fname, const char *str, size_t len,
+ int flags)
+{
+ int r;
+ sized_chunk_t c = { str, len };
+ smartlist_t *chunks = smartlist_new();
+ smartlist_add(chunks, &c);
+ r = write_chunks_to_file_impl(fname, chunks, flags);
+ smartlist_free(chunks);
+ return r;
+}
+
+/** As write_str_to_file, but does not assume a NUL-terminated
+ * string. Instead, we write <b>len</b> bytes, starting at <b>str</b>. */
+MOCK_IMPL(int,
+write_bytes_to_file,(const char *fname, const char *str, size_t len,
+ int bin))
+{
+ return write_bytes_to_file_impl(fname, str, len,
+ OPEN_FLAGS_REPLACE|(bin?O_BINARY:O_TEXT));
+}
+
+/** As write_bytes_to_file, but if the file already exists, append the bytes
+ * to the end of the file instead of overwriting it. */
+int
+append_bytes_to_file(const char *fname, const char *str, size_t len,
+ int bin)
+{
+ return write_bytes_to_file_impl(fname, str, len,
+ OPEN_FLAGS_APPEND|(bin?O_BINARY:O_TEXT));
+}
+
+/** Like write_str_to_file(), but also return -1 if there was a file
+ already residing in <b>fname</b>. */
+int
+write_bytes_to_new_file(const char *fname, const char *str, size_t len,
+ int bin)
+{
+ return write_bytes_to_file_impl(fname, str, len,
+ OPEN_FLAGS_DONT_REPLACE|
+ (bin?O_BINARY:O_TEXT));
+}
+
+/**
+ * Read the contents of the open file <b>fd</b> presuming it is a FIFO
+ * (or similar) file descriptor for which the size of the file isn't
+ * known ahead of time. Return NULL on failure, and a NUL-terminated
+ * string on success. On success, set <b>sz_out</b> to the number of
+ * bytes read.
+ */
+char *
+read_file_to_str_until_eof(int fd, size_t max_bytes_to_read, size_t *sz_out)
+{
+ ssize_t r;
+ size_t pos = 0;
+ char *string = NULL;
+ size_t string_max = 0;
+
+ if (max_bytes_to_read+1 >= SIZE_T_CEILING) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ do {
+ /* XXXX This "add 1K" approach is a little goofy; if we care about
+ * performance here, we should be doubling. But in practice we shouldn't
+ * be using this function on big files anyway. */
+ string_max = pos + 1024;
+ if (string_max > max_bytes_to_read)
+ string_max = max_bytes_to_read + 1;
+ string = tor_realloc(string, string_max);
+ r = read(fd, string + pos, string_max - pos - 1);
+ if (r < 0) {
+ int save_errno = errno;
+ tor_free(string);
+ errno = save_errno;
+ return NULL;
+ }
+
+ pos += r;
+ } while (r > 0 && pos < max_bytes_to_read);
+
+ tor_assert(pos < string_max);
+ *sz_out = pos;
+ string[pos] = '\0';
+ return string;
+}
+
+/** Read the contents of <b>filename</b> into a newly allocated
+ * string; return the string on success or NULL on failure.
+ *
+ * If <b>stat_out</b> is provided, store the result of stat()ing the
+ * file into <b>stat_out</b>.
+ *
+ * If <b>flags</b> & RFTS_BIN, open the file in binary mode.
+ * If <b>flags</b> & RFTS_IGNORE_MISSING, don't warn if the file
+ * doesn't exist.
+ */
+/*
+ * This function <em>may</em> return an erroneous result if the file
+ * is modified while it is running, but must not crash or overflow.
+ * Right now, the error case occurs when the file length grows between
+ * the call to stat and the call to read_all: the resulting string will
+ * be truncated.
+ */
+MOCK_IMPL(char *,
+read_file_to_str, (const char *filename, int flags, struct stat *stat_out))
+{
+ int fd; /* router file */
+ struct stat statbuf;
+ char *string;
+ ssize_t r;
+ int bin = flags & RFTS_BIN;
+
+ tor_assert(filename);
+
+ fd = tor_open_cloexec(filename,O_RDONLY|(bin?O_BINARY:O_TEXT),0);
+ if (fd<0) {
+ int severity = LOG_WARN;
+ int save_errno = errno;
+ if (errno == ENOENT && (flags & RFTS_IGNORE_MISSING))
+ severity = LOG_INFO;
+ log_fn(severity, LD_FS,"Could not open \"%s\": %s",filename,
+ strerror(errno));
+ errno = save_errno;
+ return NULL;
+ }
+
+ if (fstat(fd, &statbuf)<0) {
+ int save_errno = errno;
+ close(fd);
+ log_warn(LD_FS,"Could not fstat \"%s\".",filename);
+ errno = save_errno;
+ return NULL;
+ }
+
+#ifndef _WIN32
+/** When we detect that we're reading from a FIFO, don't read more than
+ * this many bytes. It's insane overkill for most uses. */
+#define FIFO_READ_MAX (1024*1024)
+ if (S_ISFIFO(statbuf.st_mode)) {
+ size_t sz = 0;
+ string = read_file_to_str_until_eof(fd, FIFO_READ_MAX, &sz);
+ int save_errno = errno;
+ if (string && stat_out) {
+ statbuf.st_size = sz;
+ memcpy(stat_out, &statbuf, sizeof(struct stat));
+ }
+ close(fd);
+ if (!string)
+ errno = save_errno;
+ return string;
+ }
+#endif /* !defined(_WIN32) */
+
+ if ((uint64_t)(statbuf.st_size)+1 >= SIZE_T_CEILING) {
+ close(fd);
+ errno = EINVAL;
+ return NULL;
+ }
+
+ string = tor_malloc((size_t)(statbuf.st_size+1));
+
+ r = read_all_from_fd(fd,string,(size_t)statbuf.st_size);
+ if (r<0) {
+ int save_errno = errno;
+ log_warn(LD_FS,"Error reading from file \"%s\": %s", filename,
+ strerror(errno));
+ tor_free(string);
+ close(fd);
+ errno = save_errno;
+ return NULL;
+ }
+ string[r] = '\0'; /* NUL-terminate the result. */
+
+#if defined(_WIN32) || defined(__CYGWIN__)
+ if (!bin && strchr(string, '\r')) {
+ log_debug(LD_FS, "We didn't convert CRLF to LF as well as we hoped "
+ "when reading %s. Coping.",
+ filename);
+ tor_strstrip(string, "\r");
+ r = strlen(string);
+ }
+ if (!bin) {
+ statbuf.st_size = (size_t) r;
+ } else
+#endif /* defined(_WIN32) || defined(__CYGWIN__) */
+ if (r != statbuf.st_size) {
+ /* Unless we're using text mode on win32, we'd better have an exact
+ * match for size. */
+ int save_errno = errno;
+ log_warn(LD_FS,"Could read only %d of %ld bytes of file \"%s\".",
+ (int)r, (long)statbuf.st_size,filename);
+ tor_free(string);
+ close(fd);
+ errno = save_errno;
+ return NULL;
+ }
+ close(fd);
+ if (stat_out) {
+ memcpy(stat_out, &statbuf, sizeof(struct stat));
+ }
+
+ return string;
+}
diff --git a/src/lib/fs/files.h b/src/lib/fs/files.h
new file mode 100644
index 000000000..be4ec485f
--- /dev/null
+++ b/src/lib/fs/files.h
@@ -0,0 +1,100 @@
+/* Copyright (c) 2003-2004, Roger Dingledine
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#ifndef TOR_FS_H
+#define TOR_FS_H
+
+#include "lib/cc/compat_compiler.h"
+#include "lib/cc/torint.h"
+#include "lib/testsupport/testsupport.h"
+
+#include <stddef.h>
+#include <stdio.h>
+
+#ifdef _WIN32
+/* We need these for struct stat to work */
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#endif
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+#ifndef O_TEXT
+#define O_TEXT 0
+#endif
+#ifndef O_NOFOLLOW
+#define O_NOFOLLOW 0
+#endif
+
+struct stat;
+
+int tor_open_cloexec(const char *path, int flags, unsigned mode);
+FILE *tor_fopen_cloexec(const char *path, const char *mode);
+int tor_rename(const char *path_old, const char *path_new);
+
+int replace_file(const char *from, const char *to);
+int touch_file(const char *fname);
+
+MOCK_DECL(int,tor_unlink,(const char *pathname));
+
+/** Return values from file_status(); see that function's documentation
+ * for details. */
+typedef enum { FN_ERROR, FN_NOENT, FN_FILE, FN_DIR, FN_EMPTY } file_status_t;
+
+file_status_t file_status(const char *filename);
+
+int64_t tor_get_avail_disk_space(const char *path);
+
+ssize_t write_all_to_fd(int fd, const char *buf, size_t count);
+ssize_t read_all_from_fd(int fd, char *buf, size_t count);
+
+#define OPEN_FLAGS_REPLACE (O_WRONLY|O_CREAT|O_TRUNC)
+#define OPEN_FLAGS_APPEND (O_WRONLY|O_CREAT|O_APPEND)
+#define OPEN_FLAGS_DONT_REPLACE (O_CREAT|O_EXCL|O_APPEND|O_WRONLY)
+typedef struct open_file_t open_file_t;
+int start_writing_to_file(const char *fname, int open_flags, int mode,
+ open_file_t **data_out);
+FILE *start_writing_to_stdio_file(const char *fname, int open_flags, int mode,
+ open_file_t **data_out);
+FILE *fdopen_file(open_file_t *file_data);
+int finish_writing_to_file(open_file_t *file_data);
+int abort_writing_to_file(open_file_t *file_data);
+MOCK_DECL(int, write_str_to_file,(const char *fname, const char *str,
+ int bin));
+MOCK_DECL(int, write_bytes_to_file,(const char *fname, const char *str,
+ size_t len,int bin));
+
+/** An ad-hoc type to hold a string of characters and a count; used by
+ * write_chunks_to_file. */
+typedef struct sized_chunk_t {
+ const char *bytes;
+ size_t len;
+} sized_chunk_t;
+struct smartlist_t;
+int write_chunks_to_file(const char *fname, const struct smartlist_t *chunks,
+ int bin, int no_tempfile);
+int append_bytes_to_file(const char *fname, const char *str, size_t len,
+ int bin);
+int write_bytes_to_new_file(const char *fname, const char *str, size_t len,
+ int bin);
+
+/** Flag for read_file_to_str: open the file in binary mode. */
+#define RFTS_BIN 1
+/** Flag for read_file_to_str: it's okay if the file doesn't exist. */
+#define RFTS_IGNORE_MISSING 2
+
+MOCK_DECL_ATTR(char *, read_file_to_str,(const char *filename, int flags,
+ struct stat *stat_out),
+ ATTR_MALLOC);
+char *read_file_to_str_until_eof(int fd, size_t max_bytes_to_read,
+ size_t *sz_out)
+ ATTR_MALLOC;
+
+#endif
diff --git a/src/lib/fs/include.am b/src/lib/fs/include.am
new file mode 100644
index 000000000..35dba6068
--- /dev/null
+++ b/src/lib/fs/include.am
@@ -0,0 +1,25 @@
+
+noinst_LIBRARIES += src/lib/libtor-fs.a
+
+if UNITTESTS_ENABLED
+noinst_LIBRARIES += src/lib/libtor-fs-testing.a
+endif
+
+src_lib_libtor_fs_a_SOURCES = \
+ src/lib/fs/dir.c \
+ src/lib/fs/files.c \
+ src/lib/fs/mmap.c \
+ src/lib/fs/path.c \
+ src/lib/fs/userdb.c
+
+src_lib_libtor_fs_testing_a_SOURCES = \
+ $(src_lib_libtor_fs_a_SOURCES)
+src_lib_libtor_fs_testing_a_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS)
+src_lib_libtor_fs_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
+
+noinst_HEADERS += \
+ src/lib/fs/dir.h \
+ src/lib/fs/files.h \
+ src/lib/fs/mmap.h \
+ src/lib/fs/path.h \
+ src/lib/fs/userdb.h
diff --git a/src/lib/fs/mmap.c b/src/lib/fs/mmap.c
new file mode 100644
index 000000000..6d69fd5e7
--- /dev/null
+++ b/src/lib/fs/mmap.c
@@ -0,0 +1,234 @@
+/* Copyright (c) 2003-2004, Roger Dingledine
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "lib/fs/mmap.h"
+#include "lib/fs/files.h"
+#include "lib/log/torlog.h"
+#include "lib/log/util_bug.h"
+#include "lib/log/win32err.h"
+#include "lib/string/compat_string.h"
+#include "lib/malloc/util_malloc.h"
+
+#ifdef HAVE_MMAP
+#include <sys/mman.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#ifdef _WIN32
+#include <windows.h>
+#endif
+
+#include <errno.h>
+#include <string.h>
+
+#if defined(HAVE_MMAP) || defined(RUNNING_DOXYGEN)
+/** Try to create a memory mapping for <b>filename</b> and return it. On
+ * failure, return NULL. Sets errno properly, using ERANGE to mean
+ * "empty file". Must only be called on trusted Tor-owned files, as changing
+ * the underlying file's size causes unspecified behavior. */
+tor_mmap_t *
+tor_mmap_file(const char *filename)
+{
+ int fd; /* router file */
+ char *string;
+ int result;
+ tor_mmap_t *res;
+ size_t size, filesize;
+ struct stat st;
+
+ tor_assert(filename);
+
+ fd = tor_open_cloexec(filename, O_RDONLY, 0);
+ if (fd<0) {
+ int save_errno = errno;
+ int severity = (errno == ENOENT) ? LOG_INFO : LOG_WARN;
+ log_fn(severity, LD_FS,"Could not open \"%s\" for mmap(): %s",filename,
+ strerror(errno));
+ errno = save_errno;
+ return NULL;
+ }
+
+ /* Get the size of the file */
+ result = fstat(fd, &st);
+ if (result != 0) {
+ int save_errno = errno;
+ log_warn(LD_FS,
+ "Couldn't fstat opened descriptor for \"%s\" during mmap: %s",
+ filename, strerror(errno));
+ close(fd);
+ errno = save_errno;
+ return NULL;
+ }
+ size = filesize = (size_t)(st.st_size);
+
+ if (st.st_size > SSIZE_T_CEILING || (off_t)size < st.st_size) {
+ log_warn(LD_FS, "File \"%s\" is too large. Ignoring.",filename);
+ errno = EFBIG;
+ close(fd);
+ return NULL;
+ }
+ if (!size) {
+ /* Zero-length file. If we call mmap on it, it will succeed but
+ * return NULL, and bad things will happen. So just fail. */
+ log_info(LD_FS,"File \"%s\" is empty. Ignoring.",filename);
+ errno = ERANGE;
+ close(fd);
+ return NULL;
+ }
+
+ string = mmap(0, size, PROT_READ, MAP_PRIVATE, fd, 0);
+ close(fd);
+ if (string == MAP_FAILED) {
+ int save_errno = errno;
+ log_warn(LD_FS,"Could not mmap file \"%s\": %s", filename,
+ strerror(errno));
+ errno = save_errno;
+ return NULL;
+ }
+
+ res = tor_malloc_zero(sizeof(tor_mmap_t));
+ res->data = string;
+ res->size = filesize;
+ res->mapping_size = size;
+
+ return res;
+}
+/** Release storage held for a memory mapping; returns 0 on success,
+ * or -1 on failure (and logs a warning). */
+int
+tor_munmap_file(tor_mmap_t *handle)
+{
+ int res;
+
+ if (handle == NULL)
+ return 0;
+
+ res = munmap((char*)handle->data, handle->mapping_size);
+ if (res == 0) {
+ /* munmap() succeeded */
+ tor_free(handle);
+ } else {
+ log_warn(LD_FS, "Failed to munmap() in tor_munmap_file(): %s",
+ strerror(errno));
+ res = -1;
+ }
+
+ return res;
+}
+#elif defined(_WIN32)
+tor_mmap_t *
+tor_mmap_file(const char *filename)
+{
+ TCHAR tfilename[MAX_PATH]= {0};
+ tor_mmap_t *res = tor_malloc_zero(sizeof(tor_mmap_t));
+ int empty = 0;
+ HANDLE file_handle = INVALID_HANDLE_VALUE;
+ DWORD size_low, size_high;
+ uint64_t real_size;
+ res->mmap_handle = NULL;
+#ifdef UNICODE
+ mbstowcs(tfilename,filename,MAX_PATH);
+#else
+ strlcpy(tfilename,filename,MAX_PATH);
+#endif
+ file_handle = CreateFile(tfilename,
+ GENERIC_READ, FILE_SHARE_READ,
+ NULL,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL,
+ 0);
+
+ if (file_handle == INVALID_HANDLE_VALUE)
+ goto win_err;
+
+ size_low = GetFileSize(file_handle, &size_high);
+
+ if (size_low == INVALID_FILE_SIZE && GetLastError() != NO_ERROR) {
+ log_warn(LD_FS,"Error getting size of \"%s\".",filename);
+ goto win_err;
+ }
+ if (size_low == 0 && size_high == 0) {
+ log_info(LD_FS,"File \"%s\" is empty. Ignoring.",filename);
+ empty = 1;
+ goto err;
+ }
+ real_size = (((uint64_t)size_high)<<32) | size_low;
+ if (real_size > SIZE_MAX) {
+ log_warn(LD_FS,"File \"%s\" is too big to map; not trying.",filename);
+ goto err;
+ }
+ res->size = real_size;
+
+ res->mmap_handle = CreateFileMapping(file_handle,
+ NULL,
+ PAGE_READONLY,
+ size_high,
+ size_low,
+ NULL);
+ if (res->mmap_handle == NULL)
+ goto win_err;
+ res->data = (char*) MapViewOfFile(res->mmap_handle,
+ FILE_MAP_READ,
+ 0, 0, 0);
+ if (!res->data)
+ goto win_err;
+
+ CloseHandle(file_handle);
+ return res;
+ win_err: {
+ DWORD e = GetLastError();
+ int severity = (e == ERROR_FILE_NOT_FOUND || e == ERROR_PATH_NOT_FOUND) ?
+ LOG_INFO : LOG_WARN;
+ char *msg = format_win32_error(e);
+ log_fn(severity, LD_FS, "Couldn't mmap file \"%s\": %s", filename, msg);
+ tor_free(msg);
+ if (e == ERROR_FILE_NOT_FOUND || e == ERROR_PATH_NOT_FOUND)
+ errno = ENOENT;
+ else
+ errno = EINVAL;
+ }
+ err:
+ if (empty)
+ errno = ERANGE;
+ if (file_handle != INVALID_HANDLE_VALUE)
+ CloseHandle(file_handle);
+ tor_munmap_file(res);
+ return NULL;
+}
+
+/* Unmap the file, and return 0 for success or -1 for failure */
+int
+tor_munmap_file(tor_mmap_t *handle)
+{
+ if (handle == NULL)
+ return 0;
+
+ if (handle->data) {
+ /* This is an ugly cast, but without it, "data" in struct tor_mmap_t would
+ have to be redefined as non-const. */
+ BOOL ok = UnmapViewOfFile( (LPVOID) handle->data);
+ if (!ok) {
+ log_warn(LD_FS, "Failed to UnmapViewOfFile() in tor_munmap_file(): %d",
+ (int)GetLastError());
+ }
+ }
+
+ if (handle->mmap_handle != NULL)
+ CloseHandle(handle->mmap_handle);
+ tor_free(handle);
+
+ return 0;
+}
+#else
+#error "cannot implement tor_mmap_file"
+#endif /* defined(HAVE_MMAP) || ... || ... */
diff --git a/src/lib/fs/mmap.h b/src/lib/fs/mmap.h
new file mode 100644
index 000000000..b0585775f
--- /dev/null
+++ b/src/lib/fs/mmap.h
@@ -0,0 +1,35 @@
+/* Copyright (c) 2003-2004, Roger Dingledine
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#ifndef TOR_MMAP_H
+#define TOR_MMAP_H
+
+#include "lib/cc/compat_compiler.h"
+#include <stddef.h>
+
+#ifdef _WIN32
+#include <windef.h>
+#endif
+
+/** Represents an mmaped file. Allocated via tor_mmap_file; freed with
+ * tor_munmap_file. */
+typedef struct tor_mmap_t {
+ const char *data; /**< Mapping of the file's contents. */
+ size_t size; /**< Size of the file. */
+
+ /* None of the fields below should be accessed from outside compat.c */
+#ifdef HAVE_MMAP
+ size_t mapping_size; /**< Size of the actual mapping. (This is this file
+ * size, rounded up to the nearest page.) */
+#elif defined _WIN32
+ HANDLE mmap_handle;
+#endif /* defined(HAVE_MMAP) || ... */
+
+} tor_mmap_t;
+
+tor_mmap_t *tor_mmap_file(const char *filename) ATTR_NONNULL((1));
+int tor_munmap_file(tor_mmap_t *handle) ATTR_NONNULL((1));
+
+#endif
diff --git a/src/lib/fs/path.c b/src/lib/fs/path.c
new file mode 100644
index 000000000..68cda6776
--- /dev/null
+++ b/src/lib/fs/path.c
@@ -0,0 +1,289 @@
+/* Copyright (c) 2003, Roger Dingledine
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "lib/fs/path.h"
+#include "lib/malloc/util_malloc.h"
+#include "lib/log/torlog.h"
+#include "lib/log/util_bug.h"
+#include "lib/string/printf.h"
+#include "lib/string/util_string.h"
+#include "lib/string/compat_ctype.h"
+#include "lib/fs/userdb.h"
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <errno.h>
+#include <string.h>
+
+/** Removes enclosing quotes from <b>path</b> and unescapes quotes between the
+ * enclosing quotes. Backslashes are not unescaped. Return the unquoted
+ * <b>path</b> on success or 0 if <b>path</b> is not quoted correctly. */
+char *
+get_unquoted_path(const char *path)
+{
+ size_t len = strlen(path);
+
+ if (len == 0) {
+ return tor_strdup("");
+ }
+
+ int has_start_quote = (path[0] == '\"');
+ int has_end_quote = (len > 0 && path[len-1] == '\"');
+ if (has_start_quote != has_end_quote || (len == 1 && has_start_quote)) {
+ return NULL;
+ }
+
+ char *unquoted_path = tor_malloc(len - has_start_quote - has_end_quote + 1);
+ char *s = unquoted_path;
+ size_t i;
+ for (i = has_start_quote; i < len - has_end_quote; i++) {
+ if (path[i] == '\"' && (i > 0 && path[i-1] == '\\')) {
+ *(s-1) = path[i];
+ } else if (path[i] != '\"') {
+ *s++ = path[i];
+ } else { /* unescaped quote */
+ tor_free(unquoted_path);
+ return NULL;
+ }
+ }
+ *s = '\0';
+ return unquoted_path;
+}
+
+/** Expand any homedir prefix on <b>filename</b>; return a newly allocated
+ * string. */
+char *
+expand_filename(const char *filename)
+{
+ tor_assert(filename);
+#ifdef _WIN32
+ /* Might consider using GetFullPathName() as described here:
+ * http://etutorials.org/Programming/secure+programming/
+ * Chapter+3.+Input+Validation/3.7+Validating+Filenames+and+Paths/
+ */
+ return tor_strdup(filename);
+#else /* !(defined(_WIN32)) */
+ if (*filename == '~') {
+ char *home, *result=NULL;
+ const char *rest;
+
+ if (filename[1] == '/' || filename[1] == '\0') {
+ home = getenv("HOME");
+ if (!home) {
+ log_warn(LD_CONFIG, "Couldn't find $HOME environment variable while "
+ "expanding \"%s\"; defaulting to \"\".", filename);
+ home = tor_strdup("");
+ } else {
+ home = tor_strdup(home);
+ }
+ rest = strlen(filename)>=2?(filename+2):"";
+ } else {
+#ifdef HAVE_PWD_H
+ char *username, *slash;
+ slash = strchr(filename, '/');
+ if (slash)
+ username = tor_strndup(filename+1,slash-filename-1);
+ else
+ username = tor_strdup(filename+1);
+ if (!(home = get_user_homedir(username))) {
+ log_warn(LD_CONFIG,"Couldn't get homedir for \"%s\"",username);
+ tor_free(username);
+ return NULL;
+ }
+ tor_free(username);
+ rest = slash ? (slash+1) : "";
+#else /* !(defined(HAVE_PWD_H)) */
+ log_warn(LD_CONFIG, "Couldn't expand homedir on system without pwd.h");
+ return tor_strdup(filename);
+#endif /* defined(HAVE_PWD_H) */
+ }
+ tor_assert(home);
+ /* Remove trailing slash. */
+ if (strlen(home)>1 && !strcmpend(home,PATH_SEPARATOR)) {
+ home[strlen(home)-1] = '\0';
+ }
+ tor_asprintf(&result,"%s"PATH_SEPARATOR"%s",home,rest);
+ tor_free(home);
+ return result;
+ } else {
+ return tor_strdup(filename);
+ }
+#endif /* defined(_WIN32) */
+}
+
+/** Return true iff <b>filename</b> is a relative path. */
+int
+path_is_relative(const char *filename)
+{
+ if (filename && filename[0] == '/')
+ return 0;
+#ifdef _WIN32
+ else if (filename && filename[0] == '\\')
+ return 0;
+ else if (filename && strlen(filename)>3 && TOR_ISALPHA(filename[0]) &&
+ filename[1] == ':' && filename[2] == '\\')
+ return 0;
+#endif /* defined(_WIN32) */
+ else
+ return 1;
+}
+
+/** Clean up <b>name</b> so that we can use it in a call to "stat". On Unix,
+ * we do nothing. On Windows, we remove a trailing slash, unless the path is
+ * the root of a disk. */
+void
+clean_fname_for_stat(char *name)
+{
+#ifdef _WIN32
+ size_t len = strlen(name);
+ if (!len)
+ return;
+ if (name[len-1]=='\\' || name[len-1]=='/') {
+ if (len == 1 || (len==3 && name[1]==':'))
+ return;
+ name[len-1]='\0';
+ }
+#else /* !(defined(_WIN32)) */
+ (void)name;
+#endif /* defined(_WIN32) */
+}
+
+/** Modify <b>fname</b> to contain the name of its parent directory. Doesn't
+ * actually examine the filesystem; does a purely syntactic modification.
+ *
+ * The parent of the root director is considered to be iteself.
+ *
+ * Path separators are the forward slash (/) everywhere and additionally
+ * the backslash (\) on Win32.
+ *
+ * Cuts off any number of trailing path separators but otherwise ignores
+ * them for purposes of finding the parent directory.
+ *
+ * Returns 0 if a parent directory was successfully found, -1 otherwise (fname
+ * did not have any path separators or only had them at the end).
+ * */
+int
+get_parent_directory(char *fname)
+{
+ char *cp;
+ int at_end = 1;
+ tor_assert(fname);
+#ifdef _WIN32
+ /* If we start with, say, c:, then don't consider that the start of the path
+ */
+ if (fname[0] && fname[1] == ':') {
+ fname += 2;
+ }
+#endif /* defined(_WIN32) */
+ /* Now we want to remove all path-separators at the end of the string,
+ * and to remove the end of the string starting with the path separator
+ * before the last non-path-separator. In perl, this would be
+ * s#[/]*$##; s#/[^/]*$##;
+ * on a unixy platform.
+ */
+ cp = fname + strlen(fname);
+ at_end = 1;
+ while (--cp >= fname) {
+ int is_sep = (*cp == '/'
+#ifdef _WIN32
+ || *cp == '\\'
+#endif
+ );
+ if (is_sep) {
+ if (cp == fname) {
+ /* This is the first separator in the file name; don't remove it! */
+ cp[1] = '\0';
+ return 0;
+ }
+ *cp = '\0';
+ if (! at_end)
+ return 0;
+ } else {
+ at_end = 0;
+ }
+ }
+ return -1;
+}
+
+#ifndef _WIN32
+/** Return a newly allocated string containing the output of getcwd(). Return
+ * NULL on failure. (We can't just use getcwd() into a PATH_MAX buffer, since
+ * Hurd hasn't got a PATH_MAX.)
+ */
+static char *
+alloc_getcwd(void)
+{
+#ifdef HAVE_GET_CURRENT_DIR_NAME
+ /* Glibc makes this nice and simple for us. */
+ char *cwd = get_current_dir_name();
+ char *result = NULL;
+ if (cwd) {
+ /* We make a copy here, in case tor_malloc() is not malloc(). */
+ result = tor_strdup(cwd);
+ raw_free(cwd); // alias for free to avoid tripping check-spaces.
+ }
+ return result;
+#else /* !(defined(HAVE_GET_CURRENT_DIR_NAME)) */
+ size_t size = 1024;
+ char *buf = NULL;
+ char *ptr = NULL;
+
+ while (ptr == NULL) {
+ buf = tor_realloc(buf, size);
+ ptr = getcwd(buf, size);
+
+ if (ptr == NULL && errno != ERANGE) {
+ tor_free(buf);
+ return NULL;
+ }
+
+ size *= 2;
+ }
+ return buf;
+#endif /* defined(HAVE_GET_CURRENT_DIR_NAME) */
+}
+#endif /* !defined(_WIN32) */
+
+/** Expand possibly relative path <b>fname</b> to an absolute path.
+ * Return a newly allocated string, possibly equal to <b>fname</b>. */
+char *
+make_path_absolute(char *fname)
+{
+#ifdef _WIN32
+ char *absfname_malloced = _fullpath(NULL, fname, 1);
+
+ /* We don't want to assume that tor_free can free a string allocated
+ * with malloc. On failure, return fname (it's better than nothing). */
+ char *absfname = tor_strdup(absfname_malloced ? absfname_malloced : fname);
+ if (absfname_malloced) raw_free(absfname_malloced);
+
+ return absfname;
+#else /* !(defined(_WIN32)) */
+ char *absfname = NULL, *path = NULL;
+
+ tor_assert(fname);
+
+ if (fname[0] == '/') {
+ absfname = tor_strdup(fname);
+ } else {
+ path = alloc_getcwd();
+ if (path) {
+ tor_asprintf(&absfname, "%s/%s", path, fname);
+ tor_free(path);
+ } else {
+ /* LCOV_EXCL_START Can't make getcwd fail. */
+ /* If getcwd failed, the best we can do here is keep using the
+ * relative path. (Perhaps / isn't readable by this UID/GID.) */
+ log_warn(LD_GENERAL, "Unable to find current working directory: %s",
+ strerror(errno));
+ absfname = tor_strdup(fname);
+ /* LCOV_EXCL_STOP */
+ }
+ }
+ return absfname;
+#endif /* defined(_WIN32) */
+}
diff --git a/src/lib/fs/path.h b/src/lib/fs/path.h
new file mode 100644
index 000000000..a3073a99e
--- /dev/null
+++ b/src/lib/fs/path.h
@@ -0,0 +1,24 @@
+/* Copyright (c) 2003-2004, Roger Dingledine
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#ifndef TOR_PATH_H
+#define TOR_PATH_H
+
+#include "lib/cc/compat_compiler.h"
+
+#ifdef _WIN32
+#define PATH_SEPARATOR "\\"
+#else
+#define PATH_SEPARATOR "/"
+#endif
+
+char *get_unquoted_path(const char *path);
+char *expand_filename(const char *filename);
+int path_is_relative(const char *filename);
+void clean_fname_for_stat(char *name);
+int get_parent_directory(char *fname);
+char *make_path_absolute(char *fname);
+
+#endif
diff --git a/src/lib/fs/userdb.c b/src/lib/fs/userdb.c
new file mode 100644
index 000000000..b7abbc781
--- /dev/null
+++ b/src/lib/fs/userdb.c
@@ -0,0 +1,132 @@
+/* Copyright (c) 2003-2004, Roger Dingledine
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "lib/fs/userdb.h"
+
+#ifndef _WIN32
+#include "lib/malloc/util_malloc.h"
+#include "lib/log/torlog.h"
+#include "lib/log/util_bug.h"
+
+#include <pwd.h>
+#include <stddef.h>
+#include <string.h>
+
+/** Cached struct from the last getpwname() call we did successfully. */
+static struct passwd *passwd_cached = NULL;
+
+/** Helper: copy a struct passwd object.
+ *
+ * We only copy the fields pw_uid, pw_gid, pw_name, pw_dir. Tor doesn't use
+ * any others, and I don't want to run into incompatibilities.
+ */
+static struct passwd *
+tor_passwd_dup(const struct passwd *pw)
+{
+ struct passwd *new_pw = tor_malloc_zero(sizeof(struct passwd));
+ if (pw->pw_name)
+ new_pw->pw_name = tor_strdup(pw->pw_name);
+ if (pw->pw_dir)
+ new_pw->pw_dir = tor_strdup(pw->pw_dir);
+ new_pw->pw_uid = pw->pw_uid;
+ new_pw->pw_gid = pw->pw_gid;
+
+ return new_pw;
+}
+
+#define tor_passwd_free(pw) \
+ FREE_AND_NULL(struct passwd, tor_passwd_free_, (pw))
+
+/** Helper: free one of our cached 'struct passwd' values. */
+static void
+tor_passwd_free_(struct passwd *pw)
+{
+ if (!pw)
+ return;
+
+ tor_free(pw->pw_name);
+ tor_free(pw->pw_dir);
+ tor_free(pw);
+}
+
+/** Wrapper around getpwnam() that caches result. Used so that we don't need
+ * to give the sandbox access to /etc/passwd.
+ *
+ * The following fields alone will definitely be copied in the output: pw_uid,
+ * pw_gid, pw_name, pw_dir. Other fields are not present in cached values.
+ *
+ * When called with a NULL argument, this function clears storage associated
+ * with static variables it uses.
+ **/
+const struct passwd *
+tor_getpwnam(const char *username)
+{
+ struct passwd *pw;
+
+ if (username == NULL) {
+ tor_passwd_free(passwd_cached);
+ passwd_cached = NULL;
+ return NULL;
+ }
+
+ if ((pw = getpwnam(username))) {
+ tor_passwd_free(passwd_cached);
+ passwd_cached = tor_passwd_dup(pw);
+ log_info(LD_GENERAL, "Caching new entry %s for %s",
+ passwd_cached->pw_name, username);
+ return pw;
+ }
+
+ /* Lookup failed */
+ if (! passwd_cached || ! passwd_cached->pw_name)
+ return NULL;
+
+ if (! strcmp(username, passwd_cached->pw_name))
+ return passwd_cached; // LCOV_EXCL_LINE - would need to make getpwnam flaky
+
+ return NULL;
+}
+
+/** Wrapper around getpwnam() that can use cached result from
+ * tor_getpwnam(). Used so that we don't need to give the sandbox access to
+ * /etc/passwd.
+ *
+ * The following fields alone will definitely be copied in the output: pw_uid,
+ * pw_gid, pw_name, pw_dir. Other fields are not present in cached values.
+ */
+const struct passwd *
+tor_getpwuid(uid_t uid)
+{
+ struct passwd *pw;
+
+ if ((pw = getpwuid(uid))) {
+ return pw;
+ }
+
+ /* Lookup failed */
+ if (! passwd_cached)
+ return NULL;
+
+ if (uid == passwd_cached->pw_uid)
+ return passwd_cached; // LCOV_EXCL_LINE - would need to make getpwnam flaky
+
+ return NULL;
+}
+
+/** Allocate and return a string containing the home directory for the
+ * user <b>username</b>. Only works on posix-like systems. */
+char *
+get_user_homedir(const char *username)
+{
+ const struct passwd *pw;
+ tor_assert(username);
+
+ if (!(pw = tor_getpwnam(username))) {
+ log_err(LD_CONFIG,"User \"%s\" not found.", username);
+ return NULL;
+ }
+ return tor_strdup(pw->pw_dir);
+}
+#endif /* !defined(_WIN32) */
diff --git a/src/lib/fs/userdb.h b/src/lib/fs/userdb.h
new file mode 100644
index 000000000..31c891ede
--- /dev/null
+++ b/src/lib/fs/userdb.h
@@ -0,0 +1,20 @@
+/* Copyright (c) 2003-2004, Roger Dingledine
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#ifndef TOR_USERDB_H
+#define TOR_USERDB_H
+
+#include "orconfig.h"
+
+#ifndef _WIN32
+#include <sys/types.h>
+
+struct passwd;
+const struct passwd *tor_getpwnam(const char *username);
+const struct passwd *tor_getpwuid(uid_t uid);
+char *get_user_homedir(const char *username);
+#endif
+
+#endif
1
0
commit 000de2f2ac4f740be8351673691371d78a5da7e9
Merge: 211360371 3d606dddb
Author: Nick Mathewson <nickm(a)torproject.org>
Date: Wed Jun 27 14:45:17 2018 -0400
Merge branch 'fs_refactor'
.gitignore | 2 +
Makefile.am | 2 +
configure.ac | 2 +-
src/common/compat.c | 601 ---------------------------
src/common/compat.h | 56 +--
src/common/util.c | 1030 ----------------------------------------------
src/common/util.h | 91 +---
src/include.am | 1 +
src/lib/fdio/fdio.c | 2 +-
src/lib/fdio/fdio.h | 2 +-
src/lib/fs/.may_include | 11 +
src/lib/fs/dir.c | 360 ++++++++++++++++
src/lib/fs/dir.h | 27 ++
src/lib/fs/files.c | 711 ++++++++++++++++++++++++++++++++
src/lib/fs/files.h | 100 +++++
src/lib/fs/include.am | 25 ++
src/lib/fs/mmap.c | 234 +++++++++++
src/lib/fs/mmap.h | 35 ++
src/lib/fs/path.c | 289 +++++++++++++
src/lib/fs/path.h | 24 ++
src/lib/fs/userdb.c | 132 ++++++
src/lib/fs/userdb.h | 20 +
src/lib/log/include.am | 7 +-
src/lib/log/torlog.c | 4 +-
src/lib/log/win32err.c | 56 +++
src/lib/log/win32err.h | 17 +
src/lib/net/.may_include | 1 +
src/lib/net/socket.c | 44 ++
src/lib/net/socket.h | 3 +
29 files changed, 2119 insertions(+), 1770 deletions(-)
diff --cc Makefile.am
index c10790c52,f43b2ad1f..ef727e01f
--- a/Makefile.am
+++ b/Makefile.am
@@@ -40,8 -40,8 +40,9 @@@ endi
# "Common" libraries used to link tor's utility code.
TOR_UTIL_LIBS = \
src/common/libor.a \
+ src/lib/libtor-fs.a \
src/lib/libtor-sandbox.a \
+ src/lib/libtor-container.a \
src/lib/libtor-net.a \
src/lib/libtor-log.a \
src/lib/libtor-lock.a \
@@@ -58,8 -58,8 +59,9 @@@
# and tests)
TOR_UTIL_TESTING_LIBS = \
src/common/libor-testing.a \
+ src/lib/libtor-fs-testing.a \
src/lib/libtor-sandbox-testing.a \
+ src/lib/libtor-container-testing.a \
src/lib/libtor-net-testing.a \
src/lib/libtor-log-testing.a \
src/lib/libtor-lock-testing.a \
1
0

27 Jun '18
commit 356f59b1bd66dc1db44e438846aaf431381743db
Author: Nick Mathewson <nickm(a)torproject.org>
Date: Wed Jun 27 10:50:24 2018 -0400
Move read/write_all_to_socket into lib/net.
---
src/common/util.c | 43 -------------------------------------------
src/common/util.h | 2 --
src/lib/net/.may_include | 1 +
src/lib/net/socket.c | 44 ++++++++++++++++++++++++++++++++++++++++++++
src/lib/net/socket.h | 3 +++
5 files changed, 48 insertions(+), 45 deletions(-)
diff --git a/src/common/util.c b/src/common/util.c
index 2d426bb13..940f25e27 100644
--- a/src/common/util.c
+++ b/src/common/util.c
@@ -1094,24 +1094,6 @@ write_all_to_fd(int fd, const char *buf, size_t count)
return (ssize_t)count;
}
-/** Write <b>count</b> bytes from <b>buf</b> to <b>sock</b>. Return the number
- * of bytes written, or -1 on error. Only use if fd is a blocking fd. */
-ssize_t
-write_all_to_socket(tor_socket_t fd, const char *buf, size_t count)
-{
- size_t written = 0;
- ssize_t result;
- raw_assert(count < SSIZE_MAX);
-
- while (written != count) {
- result = tor_socket_send(fd, buf+written, count-written, 0);
- if (result<0)
- return -1;
- written += result;
- }
- return (ssize_t)count;
-}
-
/** Read from <b>fd</b> to <b>buf</b>, until we get <b>count</b> bytes or
* reach the end of the file. Return the number of bytes read, or -1 on
* error. Only use if fd is a blocking fd. */
@@ -1137,31 +1119,6 @@ read_all_from_fd(int fd, char *buf, size_t count)
return (ssize_t)numread;
}
-/** Read from <b>sock</b> to <b>buf</b>, until we get <b>count</b> bytes or
- * reach the end of the file. Return the number of bytes read, or -1 on
- * error. Only use if fd is a blocking fd. */
-ssize_t
-read_all_from_socket(tor_socket_t sock, char *buf, size_t count)
-{
- size_t numread = 0;
- ssize_t result;
-
- if (count > SIZE_T_CEILING || count > SSIZE_MAX) {
- errno = EINVAL;
- return -1;
- }
-
- while (numread < count) {
- result = tor_socket_recv(sock, buf+numread, count-numread, 0);
- if (result<0)
- return -1;
- else if (result == 0)
- break;
- numread += result;
- }
- return (ssize_t)numread;
-}
-
/*
* Filesystem operations.
*/
diff --git a/src/common/util.h b/src/common/util.h
index 1cb3e73b3..b7ac2a176 100644
--- a/src/common/util.h
+++ b/src/common/util.h
@@ -118,9 +118,7 @@ int format_time_interval(char *out, size_t out_len, long interval);
/* File helpers */
ssize_t write_all_to_fd(int fd, const char *buf, size_t count);
-ssize_t write_all_to_socket(tor_socket_t fd, const char *buf, size_t count);
ssize_t read_all_from_fd(int fd, char *buf, size_t count);
-ssize_t read_all_from_socket(tor_socket_t fd, char *buf, size_t count);
#define write_all(fd, buf, count, isSock) \
((isSock) ? write_all_to_socket((fd), (buf), (count)) \
diff --git a/src/lib/net/.may_include b/src/lib/net/.may_include
index 479f85293..1458dad99 100644
--- a/src/lib/net/.may_include
+++ b/src/lib/net/.may_include
@@ -5,6 +5,7 @@ ht.h
lib/cc/*.h
lib/container/*.h
lib/ctime/*.h
+lib/err/*.h
lib/lock/*.h
lib/log/*.h
lib/net/*.h
diff --git a/src/lib/net/socket.c b/src/lib/net/socket.c
index 4c14a4289..cd18d7083 100644
--- a/src/lib/net/socket.c
+++ b/src/lib/net/socket.c
@@ -7,6 +7,7 @@
#include "lib/net/socket.h"
#include "lib/net/address.h"
#include "lib/cc/compat_compiler.h"
+#include "lib/err/torerr.h"
#include "lib/lock/compat_mutex.h"
#include "lib/log/torlog.h"
#include "lib/log/util_bug.h"
@@ -647,3 +648,46 @@ set_socket_nonblocking(tor_socket_t sock)
return 0;
}
+
+/** Read from <b>sock</b> to <b>buf</b>, until we get <b>count</b> bytes or
+ * reach the end of the file. Return the number of bytes read, or -1 on
+ * error. Only use if fd is a blocking fd. */
+ssize_t
+read_all_from_socket(tor_socket_t sock, char *buf, size_t count)
+{
+ size_t numread = 0;
+ ssize_t result;
+
+ if (count > SIZE_T_CEILING || count > SSIZE_MAX) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ while (numread < count) {
+ result = tor_socket_recv(sock, buf+numread, count-numread, 0);
+ if (result<0)
+ return -1;
+ else if (result == 0)
+ break;
+ numread += result;
+ }
+ return (ssize_t)numread;
+}
+
+/** Write <b>count</b> bytes from <b>buf</b> to <b>sock</b>. Return the number
+ * of bytes written, or -1 on error. Only use if fd is a blocking fd. */
+ssize_t
+write_all_to_socket(tor_socket_t fd, const char *buf, size_t count)
+{
+ size_t written = 0;
+ ssize_t result;
+ raw_assert(count < SSIZE_MAX);
+
+ while (written != count) {
+ result = tor_socket_send(fd, buf+written, count-written, 0);
+ if (result<0)
+ return -1;
+ written += result;
+ }
+ return (ssize_t)count;
+}
diff --git a/src/lib/net/socket.h b/src/lib/net/socket.h
index 93af92983..99b0de4ff 100644
--- a/src/lib/net/socket.h
+++ b/src/lib/net/socket.h
@@ -52,6 +52,9 @@ int network_init(void);
int get_max_sockets(void);
void set_max_sockets(int);
+ssize_t write_all_to_socket(tor_socket_t fd, const char *buf, size_t count);
+ssize_t read_all_from_socket(tor_socket_t fd, char *buf, size_t count);
+
/* For stupid historical reasons, windows sockets have an independent
* set of errnos, and an independent way to get them. Also, you can't
* always believe WSAEWOULDBLOCK. Use the macros below to compare
1
0

[translation/support-topics_completed] Update translations for support-topics_completed
by translation@torproject.org 27 Jun '18
by translation@torproject.org 27 Jun '18
27 Jun '18
commit 1a1ba8c4ed6c04335ae11017578ea4d09d28292c
Author: Translation commit bot <translation(a)torproject.org>
Date: Wed Jun 27 18:19:43 2018 +0000
Update translations for support-topics_completed
---
bn_BD.json | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 57 insertions(+)
diff --git a/bn_BD.json b/bn_BD.json
new file mode 100644
index 000000000..11ce33952
--- /dev/null
+++ b/bn_BD.json
@@ -0,0 +1,57 @@
+{
+ "faq": {
+ "path": "#faq",
+ "control": "faq",
+ "label": "প্রায়শই জিজ্ঞাসিত প্রশ্নাবলী"
+ },
+ "tbb": {
+ "path": "#tbb",
+ "control": "tbb",
+ "label": "টর ব্রাউজার"
+ },
+ "tormessenger": {
+ "path": "#tormessenger",
+ "control": "tormessenger",
+ "label": "Tor Messenger"
+ },
+ "tormobile": {
+ "path": "#tormobile",
+ "control": "tormobile",
+ "label": "Tor Mobile"
+ },
+ "gettor": {
+ "path": "#gettor",
+ "control": "gettor",
+ "label": "GetTor"
+ },
+ "connecting": {
+ "path": "#connectingtotor",
+ "control": "connectingtotor",
+ "label": "Connecting To Tor"
+ },
+ "censorship": {
+ "path": "#censorship",
+ "control": "censorship",
+ "label": "Censorship"
+ },
+ "https": {
+ "path": "#https",
+ "control": "https",
+ "label": "HTTPS"
+ },
+ "operators": {
+ "path": "#operators",
+ "control": "operators",
+ "label": "Operators"
+ },
+ "onionservices": {
+ "path": "#onionservices",
+ "control": "onionservices",
+ "label": "Onion সেবা"
+ },
+ "misc": {
+ "path": "#misc",
+ "control": "misc",
+ "label": "Misc"
+ }
+}
1
0

[translation/support-tbb] Update translations for support-tbb
by translation@torproject.org 27 Jun '18
by translation@torproject.org 27 Jun '18
27 Jun '18
commit 4a8c7a3e1e0d7e5abacd71ff13db0226052bd98e
Author: Translation commit bot <translation(a)torproject.org>
Date: Wed Jun 27 18:19:31 2018 +0000
Update translations for support-tbb
---
bn_BD.json | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/bn_BD.json b/bn_BD.json
index b65ea1f09..c4b54e0e2 100644
--- a/bn_BD.json
+++ b/bn_BD.json
@@ -2,13 +2,13 @@
"tbb-1": {
"id": "#tbb-1",
"control": "tbb-1",
- "title": "What are the most common issues with the latest stable version of Tor Browser?",
- "description": "<p class=\"mb-3\">Whenever we release a new stable version of Tor Browser, we write a blog post that details its new features and known issues. If you started having issues with your Tor Browser after an update, check out <mark><a href=\"https://blog.torproject.org\">https://blog.torproject.org</a></mark> for the most recent stable Tor Browser post to see if your issue is listed.</p>"
+ "title": "টর ব্রাউজারের সর্বশেষ স্থিতিশীল সংস্করণে সবচেয়ে সাধারণ সমস্যা কি?",
+ "description": "<p class=\"mb-3\">যখন আমরা টর ব্রাউজারের একটি নতুন স্থিতিকাল সংস্করণটি মুক্তি দিই, আমরা একটি ব্লগ পোস্ট লিখি যা তার নতুন বৈশিষ্ট্যগুলি এবং পরিচিত সমস্যাগুলির বিবরণ দেয়। আপনি যদি আপডেটের পরে আপনার টর ব্রাউজারের সাথে সমস্যা শুরু করেন তবে আপনার সাম্প্রতিকতম টর ব্রাউজার পোস্টের জন্য <mark><a href=\"https://blog.torproject.org\">https://blog.torproject.org</a></mark> দেখুন। আপনার সমস্যা তালিকাভুক্ত কিনা তা দেখার জন্য।</p>"
},
"tbb-2": {
"id": "#tbb-2",
"control": "tbb-2",
- "title": "Why is the first IP address in my relay circuit always the same?",
+ "title": "কেন আমার রিলে সার্কিটে প্রথম IP ঠিকানা সবসময় একই?",
"description": "<p class=\"mb-3\">That is normal Tor behavior. The first relay in your circuit is called an \"entry guard\" or \"guard.\" It is a fast and stable relay that remains the first one in your circuit for 2-3 months in order to protect against a known anonymity-breaking attack. The rest of your circuit changes with every new website you visit, and all together these relays provide the full privacy protections of Tor. For more information on how guard relays work, see this <mark><a href=\"https://blog.torproject.org/improving-tors-anonymity-changing-guard-paramet…">blog post</a></mark> and <mark><a href=\"https://www-users.cs.umn.edu/~hoppernj/single_guard.pdf\">paper</a></mark> on entry guards.</p>"
},
"tbb-3": {
1
0

[webwml/staging] Bug 20628: fix download links for da, he, sv-SE, zh-TW locales
by boklm@torproject.org 27 Jun '18
by boklm@torproject.org 27 Jun '18
27 Jun '18
commit fcd641d5123a4f812398dfdc9488ae397351ab74
Author: Nicolas Vigier <boklm(a)torproject.org>
Date: Wed Jun 27 20:12:26 2018 +0200
Bug 20628: fix download links for da, he, sv-SE, zh-TW locales
---
projects/en/torbrowser.wml | 96 +++++++++++++++++++++++-----------------------
1 file changed, 48 insertions(+), 48 deletions(-)
diff --git a/projects/en/torbrowser.wml b/projects/en/torbrowser.wml
index 0d0881d6..27000d11 100644
--- a/projects/en/torbrowser.wml
+++ b/projects/en/torbrowser.wml
@@ -365,20 +365,20 @@
<tr>
<td>Dansk (da)</td>
<td>
- <a href="../dist/torbrowser/<version-torbrowserbundle>/torbrowser-install-<version-torbrowserbundlebeta>_da.exe">32/64-bit</a>
- <em>(<a href="../dist/torbrowser/<version-torbrowserbundle>/torbrowser-install-<version-torbrowserbundlebeta>_da.exe.asc">sig</a>)</em>
+ <a href="../dist/torbrowser/<version-torbrowserbundlebeta>/torbrowser-install-<version-torbrowserbundlebeta>_da.exe">32/64-bit</a>
+ <em>(<a href="../dist/torbrowser/<version-torbrowserbundlebeta>/torbrowser-install-<version-torbrowserbundlebeta>_da.exe.asc">sig</a>)</em>
•
- <a href="../dist/torbrowser/<version-torbrowserbundle>/torbrowser-install-win64-<version-torbrowserbundlebeta>_da.exe">64-bit</a>
- <em>(<a href="../dist/torbrowser/<version-torbrowserbundle>/torbrowser-install-win64-<version-torbrowserbundlebeta>_da.exe.asc">sig</a>)</em>
+ <a href="../dist/torbrowser/<version-torbrowserbundlebeta>/torbrowser-install-win64-<version-torbrowserbundlebeta>_da.exe">64-bit</a>
+ <em>(<a href="../dist/torbrowser/<version-torbrowserbundlebeta>/torbrowser-install-win64-<version-torbrowserbundlebeta>_da.exe.asc">sig</a>)</em>
</td>
- <td><a href="../dist/torbrowser/<version-torbrowserbundleosx64>/TorBrowser-<version-torbrowserbundleosx64beta>-osx64_da.dmg">64-bit</a>
- <em>(<a href="../dist/torbrowser/<version-torbrowserbundleosx64>/TorBrowser-<version-torbrowserbundleosx64beta>-osx64_da.dmg.asc">sig</a>)</em>
- <!-- EC • <a href="../dist/torbrowser/<version-torbrowserbundleosx64>/TorBrowser-<version-torbrowserbundleosx64beta>-osx-x86_64-da.zip">64-bit</a>
- <em>(<a href="../dist/torbrowser/<version-torbrowserbundleosx64>/TorBrowser-<version-torbrowserbundleosx64beta>-osx-x86_64-da.zip.asc">sig</a>)</em></td> -->
- <td><a href="../dist/torbrowser/<version-torbrowserbundlelinux32>/tor-browser-linux32-<version-torbrowserbundlelinux32beta>_da.tar.xz">32-bit</a>
- <em>(<a href="../dist/torbrowser/<version-torbrowserbundlelinux32>/tor-browser-linux32-<version-torbrowserbundlelinux32beta>_da.tar.xz.asc">sig</a>)</em>
- • <a href="../dist/torbrowser/<version-torbrowserbundlelinux64>/tor-browser-linux64-<version-torbrowserbundlelinux64beta>_da.tar.xz">64-bit</a>
- <em>(<a href="../dist/torbrowser/<version-torbrowserbundlelinux64>/tor-browser-linux64-<version-torbrowserbundlelinux64beta>_da.tar.xz.asc">sig</a>)</em></td>
+ <td><a href="../dist/torbrowser/<version-torbrowserbundleosx64beta>/TorBrowser-<version-torbrowserbundleosx64beta>-osx64_da.dmg">64-bit</a>
+ <em>(<a href="../dist/torbrowser/<version-torbrowserbundleosx64beta>/TorBrowser-<version-torbrowserbundleosx64beta>-osx64_da.dmg.asc">sig</a>)</em>
+ <!-- EC • <a href="../dist/torbrowser/<version-torbrowserbundleosx64beta>/TorBrowser-<version-torbrowserbundleosx64beta>-osx-x86_64-da.zip">64-bit</a>
+ <em>(<a href="../dist/torbrowser/<version-torbrowserbundleosx64beta>/TorBrowser-<version-torbrowserbundleosx64beta>-osx-x86_64-da.zip.asc">sig</a>)</em></td> -->
+ <td><a href="../dist/torbrowser/<version-torbrowserbundlelinux32beta>/tor-browser-linux32-<version-torbrowserbundlelinux32beta>_da.tar.xz">32-bit</a>
+ <em>(<a href="../dist/torbrowser/<version-torbrowserbundlelinux32beta>/tor-browser-linux32-<version-torbrowserbundlelinux32beta>_da.tar.xz.asc">sig</a>)</em>
+ • <a href="../dist/torbrowser/<version-torbrowserbundlelinux64beta>/tor-browser-linux64-<version-torbrowserbundlelinux64beta>_da.tar.xz">64-bit</a>
+ <em>(<a href="../dist/torbrowser/<version-torbrowserbundlelinux64beta>/tor-browser-linux64-<version-torbrowserbundlelinux64beta>_da.tar.xz.asc">sig</a>)</em></td>
</tr>
<tr>
<td>Deutsch (de)</td>
@@ -455,20 +455,20 @@
<tr>
<td>עברית (he)</td>
<td>
- <a href="../dist/torbrowser/<version-torbrowserbundle>/torbrowser-install-<version-torbrowserbundlebeta>_he.exe">32/64-bit</a>
- <em>(<a href="../dist/torbrowser/<version-torbrowserbundle>/torbrowser-install-<version-torbrowserbundlebeta>_he.exe.asc">sig</a>)</em>
+ <a href="../dist/torbrowser/<version-torbrowserbundlebeta>/torbrowser-install-<version-torbrowserbundlebeta>_he.exe">32/64-bit</a>
+ <em>(<a href="../dist/torbrowser/<version-torbrowserbundlebeta>/torbrowser-install-<version-torbrowserbundlebeta>_he.exe.asc">sig</a>)</em>
•
- <a href="../dist/torbrowser/<version-torbrowserbundle>/torbrowser-install-win64-<version-torbrowserbundlebeta>_he.exe">64-bit</a>
- <em>(<a href="../dist/torbrowser/<version-torbrowserbundle>/torbrowser-install-win64-<version-torbrowserbundlebeta>_he.exe.asc">sig</a>)</em>
+ <a href="../dist/torbrowser/<version-torbrowserbundlebeta>/torbrowser-install-win64-<version-torbrowserbundlebeta>_he.exe">64-bit</a>
+ <em>(<a href="../dist/torbrowser/<version-torbrowserbundlebeta>/torbrowser-install-win64-<version-torbrowserbundlebeta>_he.exe.asc">sig</a>)</em>
</td>
- <td><a href="../dist/torbrowser/<version-torbrowserbundleosx64>/TorBrowser-<version-torbrowserbundleosx64beta>-osx64_he.dmg">64-bit</a>
- <em>(<a href="../dist/torbrowser/<version-torbrowserbundleosx64>/TorBrowser-<version-torbrowserbundleosx64beta>-osx64_he.dmg.asc">sig</a>)</em>
- <!-- EC • <a href="../dist/torbrowser/<version-torbrowserbundleosx64>/TorBrowser-<version-torbrowserbundleosx64beta>-osx-x86_64-he.zip">64-bit</a>
- <em>(<a href="../dist/torbrowser/<version-torbrowserbundleosx64>/TorBrowser-<version-torbrowserbundleosx64beta>-osx-x86_64-he.zip.asc">sig</a>)</em></td> -->
- <td><a href="../dist/torbrowser/<version-torbrowserbundlelinux32>/tor-browser-linux32-<version-torbrowserbundlelinux32beta>_he.tar.xz">32-bit</a>
- <em>(<a href="../dist/torbrowser/<version-torbrowserbundlelinux32>/tor-browser-linux32-<version-torbrowserbundlelinux32beta>_he.tar.xz.asc">sig</a>)</em>
- • <a href="../dist/torbrowser/<version-torbrowserbundlelinux64>/tor-browser-linux64-<version-torbrowserbundlelinux64beta>_he.tar.xz">64-bit</a>
- <em>(<a href="../dist/torbrowser/<version-torbrowserbundlelinux64>/tor-browser-linux64-<version-torbrowserbundlelinux64beta>_he.tar.xz.asc">sig</a>)</em></td>
+ <td><a href="../dist/torbrowser/<version-torbrowserbundleosx64beta>/TorBrowser-<version-torbrowserbundleosx64beta>-osx64_he.dmg">64-bit</a>
+ <em>(<a href="../dist/torbrowser/<version-torbrowserbundleosx64beta>/TorBrowser-<version-torbrowserbundleosx64beta>-osx64_he.dmg.asc">sig</a>)</em>
+ <!-- EC • <a href="../dist/torbrowser/<version-torbrowserbundleosx64beta>/TorBrowser-<version-torbrowserbundleosx64beta>-osx-x86_64-he.zip">64-bit</a>
+ <em>(<a href="../dist/torbrowser/<version-torbrowserbundleosx64beta>/TorBrowser-<version-torbrowserbundleosx64beta>-osx-x86_64-he.zip.asc">sig</a>)</em></td> -->
+ <td><a href="../dist/torbrowser/<version-torbrowserbundlelinux32beta>/tor-browser-linux32-<version-torbrowserbundlelinux32beta>_he.tar.xz">32-bit</a>
+ <em>(<a href="../dist/torbrowser/<version-torbrowserbundlelinux32beta>/tor-browser-linux32-<version-torbrowserbundlelinux32beta>_he.tar.xz.asc">sig</a>)</em>
+ • <a href="../dist/torbrowser/<version-torbrowserbundlelinux64beta>/tor-browser-linux64-<version-torbrowserbundlelinux64beta>_he.tar.xz">64-bit</a>
+ <em>(<a href="../dist/torbrowser/<version-torbrowserbundlelinux64beta>/tor-browser-linux64-<version-torbrowserbundlelinux64beta>_he.tar.xz.asc">sig</a>)</em></td>
</tr>
<tr>
<td>Italiano (it)</td>
@@ -599,20 +599,20 @@
<tr>
<td>Svenska (sv-SE)</td>
<td>
- <a href="../dist/torbrowser/<version-torbrowserbundle>/torbrowser-install-<version-torbrowserbundlebeta>_sv-SE.exe">32/64-bit</a>
- <em>(<a href="../dist/torbrowser/<version-torbrowserbundle>/torbrowser-install-<version-torbrowserbundlebeta>_sv-SE.exe.asc">sig</a>)</em>
+ <a href="../dist/torbrowser/<version-torbrowserbundlebeta>/torbrowser-install-<version-torbrowserbundlebeta>_sv-SE.exe">32/64-bit</a>
+ <em>(<a href="../dist/torbrowser/<version-torbrowserbundlebeta>/torbrowser-install-<version-torbrowserbundlebeta>_sv-SE.exe.asc">sig</a>)</em>
•
- <a href="../dist/torbrowser/<version-torbrowserbundle>/torbrowser-install-win64-<version-torbrowserbundlebeta>_sv-SE.exe">64-bit</a>
- <em>(<a href="../dist/torbrowser/<version-torbrowserbundle>/torbrowser-install-win64-<version-torbrowserbundlebeta>_sv-SE.exe.asc">sig</a>)</em>
+ <a href="../dist/torbrowser/<version-torbrowserbundlebeta>/torbrowser-install-win64-<version-torbrowserbundlebeta>_sv-SE.exe">64-bit</a>
+ <em>(<a href="../dist/torbrowser/<version-torbrowserbundlebeta>/torbrowser-install-win64-<version-torbrowserbundlebeta>_sv-SE.exe.asc">sig</a>)</em>
</td>
- <td><a href="../dist/torbrowser/<version-torbrowserbundleosx64>/TorBrowser-<version-torbrowserbundleosx64beta>-osx64_sv-SE.dmg">64-bit</a>
- <em>(<a href="../dist/torbrowser/<version-torbrowserbundleosx64>/TorBrowser-<version-torbrowserbundleosx64beta>-osx64_sv-SE.dmg.asc">sig</a>)</em>
- <!-- EC • <a href="../dist/torbrowser/<version-torbrowserbundleosx64>/TorBrowser-<version-torbrowserbundleosx64beta>-osx-x86_64-sv-SE.zip">64-bit</a>
- <em>(<a href="../dist/torbrowser/<version-torbrowserbundleosx64>/TorBrowser-<version-torbrowserbundleosx64beta>-osx-x86_64-sv-SE.zip.asc">sig</a>)</em></td> -->
- <td><a href="../dist/torbrowser/<version-torbrowserbundlelinux32>/tor-browser-linux32-<version-torbrowserbundlelinux32beta>_sv-SE.tar.xz">32-bit</a>
- <em>(<a href="../dist/torbrowser/<version-torbrowserbundlelinux32>/tor-browser-linux32-<version-torbrowserbundlelinux32beta>_sv-SE.tar.xz.asc">sig</a>)</em>
- • <a href="../dist/torbrowser/<version-torbrowserbundlelinux64>/tor-browser-linux64-<version-torbrowserbundlelinux64beta>_sv-SE.tar.xz">64-bit</a>
- <em>(<a href="../dist/torbrowser/<version-torbrowserbundlelinux64>/tor-browser-linux64-<version-torbrowserbundlelinux64beta>_sv-SE.tar.xz.asc">sig</a>)</em></td>
+ <td><a href="../dist/torbrowser/<version-torbrowserbundleosx64beta>/TorBrowser-<version-torbrowserbundleosx64beta>-osx64_sv-SE.dmg">64-bit</a>
+ <em>(<a href="../dist/torbrowser/<version-torbrowserbundleosx64beta>/TorBrowser-<version-torbrowserbundleosx64beta>-osx64_sv-SE.dmg.asc">sig</a>)</em>
+ <!-- EC • <a href="../dist/torbrowser/<version-torbrowserbundleosx64beta>/TorBrowser-<version-torbrowserbundleosx64beta>-osx-x86_64-sv-SE.zip">64-bit</a>
+ <em>(<a href="../dist/torbrowser/<version-torbrowserbundleosx64beta>/TorBrowser-<version-torbrowserbundleosx64beta>-osx-x86_64-sv-SE.zip.asc">sig</a>)</em></td> -->
+ <td><a href="../dist/torbrowser/<version-torbrowserbundlelinux32beta>/tor-browser-linux32-<version-torbrowserbundlelinux32beta>_sv-SE.tar.xz">32-bit</a>
+ <em>(<a href="../dist/torbrowser/<version-torbrowserbundlelinux32beta>/tor-browser-linux32-<version-torbrowserbundlelinux32beta>_sv-SE.tar.xz.asc">sig</a>)</em>
+ • <a href="../dist/torbrowser/<version-torbrowserbundlelinux64beta>/tor-browser-linux64-<version-torbrowserbundlelinux64beta>_sv-SE.tar.xz">64-bit</a>
+ <em>(<a href="../dist/torbrowser/<version-torbrowserbundlelinux64beta>/tor-browser-linux64-<version-torbrowserbundlelinux64beta>_sv-SE.tar.xz.asc">sig</a>)</em></td>
</tr>
<tr class="alt">
<td>Türkçe (tr)</td>
@@ -671,20 +671,20 @@
<tr>
<td>正體字 (zh-TW)</td>
<td>
- <a href="../dist/torbrowser/<version-torbrowserbundle>/torbrowser-install-<version-torbrowserbundlebeta>_zh-TW.exe">32/64-bit</a>
- <em>(<a href="../dist/torbrowser/<version-torbrowserbundle>/torbrowser-install-<version-torbrowserbundlebeta>_zh-TW.exe.asc">sig</a>)</em>
+ <a href="../dist/torbrowser/<version-torbrowserbundlebeta>/torbrowser-install-<version-torbrowserbundlebeta>_zh-TW.exe">32/64-bit</a>
+ <em>(<a href="../dist/torbrowser/<version-torbrowserbundlebeta>/torbrowser-install-<version-torbrowserbundlebeta>_zh-TW.exe.asc">sig</a>)</em>
•
- <a href="../dist/torbrowser/<version-torbrowserbundle>/torbrowser-install-win64-<version-torbrowserbundlebeta>_zh-TW.exe">64-bit</a>
- <em>(<a href="../dist/torbrowser/<version-torbrowserbundle>/torbrowser-install-win64-<version-torbrowserbundlebeta>_zh-TW.exe.asc">sig</a>)</em>
+ <a href="../dist/torbrowser/<version-torbrowserbundlebeta>/torbrowser-install-win64-<version-torbrowserbundlebeta>_zh-TW.exe">64-bit</a>
+ <em>(<a href="../dist/torbrowser/<version-torbrowserbundlebeta>/torbrowser-install-win64-<version-torbrowserbundlebeta>_zh-TW.exe.asc">sig</a>)</em>
</td>
- <td><a href="../dist/torbrowser/<version-torbrowserbundleosx64>/TorBrowser-<version-torbrowserbundleosx64beta>-osx64_zh-TW.dmg">64-bit</a>
- <em>(<a href="../dist/torbrowser/<version-torbrowserbundleosx64>/TorBrowser-<version-torbrowserbundleosx64beta>-osx64_zh-TW.dmg.asc">sig</a>)</em>
- <!-- EC • <a href="../dist/torbrowser/<version-torbrowserbundleosx64>/TorBrowser-<version-torbrowserbundleosx64beta>-osx-x86_64-zh-TW.zip">64-bit</a>
- <em>(<a href="../dist/torbrowser/<version-torbrowserbundleosx64>/TorBrowser-<version-torbrowserbundleosx64beta>-osx-x86_64-zh-TW.zip.asc">sig</a>)</em></td> -->
- <td><a href="../dist/torbrowser/<version-torbrowserbundlelinux32>/tor-browser-linux32-<version-torbrowserbundlelinux32beta>_zh-TW.tar.xz">32-bit</a>
- <em>(<a href="../dist/torbrowser/<version-torbrowserbundlelinux32>/tor-browser-linux32-<version-torbrowserbundlelinux32beta>_zh-TW.tar.xz.asc">sig</a>)</em>
- • <a href="../dist/torbrowser/<version-torbrowserbundlelinux64>/tor-browser-linux64-<version-torbrowserbundlelinux64beta>_zh-TW.tar.xz">64-bit</a>
- <em>(<a href="../dist/torbrowser/<version-torbrowserbundlelinux64>/tor-browser-linux64-<version-torbrowserbundlelinux64beta>_zh-TW.tar.xz.asc">sig</a>)</em></td>
+ <td><a href="../dist/torbrowser/<version-torbrowserbundleosx64beta>/TorBrowser-<version-torbrowserbundleosx64beta>-osx64_zh-TW.dmg">64-bit</a>
+ <em>(<a href="../dist/torbrowser/<version-torbrowserbundleosx64beta>/TorBrowser-<version-torbrowserbundleosx64beta>-osx64_zh-TW.dmg.asc">sig</a>)</em>
+ <!-- EC • <a href="../dist/torbrowser/<version-torbrowserbundleosx64beta>/TorBrowser-<version-torbrowserbundleosx64beta>-osx-x86_64-zh-TW.zip">64-bit</a>
+ <em>(<a href="../dist/torbrowser/<version-torbrowserbundleosx64beta>/TorBrowser-<version-torbrowserbundleosx64beta>-osx-x86_64-zh-TW.zip.asc">sig</a>)</em></td> -->
+ <td><a href="../dist/torbrowser/<version-torbrowserbundlelinux32beta>/tor-browser-linux32-<version-torbrowserbundlelinux32beta>_zh-TW.tar.xz">32-bit</a>
+ <em>(<a href="../dist/torbrowser/<version-torbrowserbundlelinux32beta>/tor-browser-linux32-<version-torbrowserbundlelinux32beta>_zh-TW.tar.xz.asc">sig</a>)</em>
+ • <a href="../dist/torbrowser/<version-torbrowserbundlelinux64beta>/tor-browser-linux64-<version-torbrowserbundlelinux64beta>_zh-TW.tar.xz">64-bit</a>
+ <em>(<a href="../dist/torbrowser/<version-torbrowserbundlelinux64beta>/tor-browser-linux64-<version-torbrowserbundlelinux64beta>_zh-TW.tar.xz.asc">sig</a>)</em></td>
</tr>
</tbody>
</table>
1
0