Skip to content
GitLab
Menu
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
David Goulet
Tor
Commits
1e2e0f7e
Commit
1e2e0f7e
authored
Jun 27, 2018
by
Nick Mathewson
🎨
Browse files
Extract functions from compat.c and util.h into a new fs library
parent
3246c114
Changes
19
Expand all
Hide whitespace changes
Inline
Side-by-side
.gitignore
View file @
1e2e0f7e
...
...
@@ -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
...
...
Makefile.am
View file @
1e2e0f7e
...
...
@@ -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
\
...
...
src/common/compat.c
View file @
1e2e0f7e
...
...
@@ -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. */
...
...
src/common/compat.h
View file @
1e2e0f7e
...
...
@@ -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