test: improve file-win32, file-posix test coverage (#3761)

This commit is contained in:
Charles Kerr 2022-09-04 19:40:34 -05:00 committed by GitHub
parent 339b9580cf
commit ae74a13eb1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 253 additions and 242 deletions

View File

@ -116,29 +116,6 @@ static void set_system_error_if_file_found(tr_error** error, int code)
}
}
static constexpr auto stat_to_sys_path_info(struct stat const& sb)
{
auto info = tr_sys_path_info{};
if (S_ISREG(sb.st_mode))
{
info.type = TR_SYS_PATH_IS_FILE;
}
else if (S_ISDIR(sb.st_mode))
{
info.type = TR_SYS_PATH_IS_DIRECTORY;
}
else
{
info.type = TR_SYS_PATH_IS_OTHER;
}
info.size = static_cast<uint64_t>(sb.st_size);
info.last_modified_at = sb.st_mtime;
return info;
}
static void set_file_for_single_pass(tr_sys_file_t handle)
{
/* Set hints about the lookahead buffer and caching. It's okay
@ -167,126 +144,6 @@ static void set_file_for_single_pass(tr_sys_file_t handle)
errno = err;
}
#ifndef HAVE_MKDIRP
static bool create_path_require_dir(char const* path, tr_error** error)
{
struct stat sb;
if (stat(path, &sb) == -1)
{
set_system_error(error, errno);
return false;
}
if ((sb.st_mode & S_IFMT) != S_IFDIR)
{
tr_error_set(error, ENOTDIR, fmt::format(FMT_STRING("File is in the way: {:s}"), path));
return false;
}
return true;
}
static bool create_path(char const* path_in, int permissions, tr_error** error)
{
/* make a temporary copy of path */
auto path = tr_pathbuf{ path_in };
/* walk past the root */
char* p = std::data(path);
while (*p == TR_PATH_DELIMITER)
{
++p;
}
char* path_end = p + strlen(p);
while (path_end > path && *path_end == TR_PATH_DELIMITER)
{
--path_end;
}
char* pp = nullptr;
bool ret = false;
tr_error* my_error = nullptr;
/* Go one level up on each iteration and attempt to create */
for (pp = path_end; pp != nullptr; pp = strrchr(p, TR_PATH_DELIMITER))
{
*pp = '\0';
ret = mkdir(path, permissions) != -1;
if (ret)
{
break;
}
if (errno == EEXIST)
{
ret = create_path_require_dir(path, &my_error);
if (ret)
{
break;
}
goto FAILURE;
}
if (errno != ENOENT)
{
set_system_error(&my_error, errno);
goto FAILURE;
}
}
if (ret && pp == path_end)
{
goto CLEANUP;
}
/* Go one level down on each iteration and attempt to create */
for (pp = pp == nullptr ? p + strlen(p) : pp; pp < path_end; pp += strlen(pp))
{
*pp = TR_PATH_DELIMITER;
if (mkdir(path, permissions) == -1)
{
break;
}
}
ret = create_path_require_dir(path, &my_error);
if (ret)
{
goto CLEANUP;
}
FAILURE:
TR_ASSERT(!ret);
TR_ASSERT(my_error != nullptr);
tr_logAddError(fmt::format(
_("Couldn't create '{path}': {error} ({error_code})"),
fmt::arg("path", path),
fmt::arg("error", my_error->message),
fmt::arg("error_code", my_error->code)));
tr_error_propagate(error, &my_error);
CLEANUP:
TR_ASSERT(my_error == nullptr);
return ret;
}
#endif
bool tr_sys_path_exists(char const* path, tr_error** error)
{
TR_ASSERT(path != nullptr);
@ -317,13 +174,31 @@ std::optional<tr_sys_path_info> tr_sys_path_get_info(std::string_view path, int
ok = lstat(szpath, &sb) != -1;
}
if (ok)
if (!ok)
{
return stat_to_sys_path_info(sb);
set_system_error(error, errno);
return {};
}
set_system_error(error, errno);
return {};
auto info = tr_sys_path_info{};
if (S_ISREG(sb.st_mode))
{
info.type = TR_SYS_PATH_IS_FILE;
}
else if (S_ISDIR(sb.st_mode))
{
info.type = TR_SYS_PATH_IS_DIRECTORY;
}
else
{
info.type = TR_SYS_PATH_IS_OTHER;
}
info.size = static_cast<uint64_t>(sb.st_size);
info.last_modified_at = sb.st_mtime;
return info;
}
bool tr_sys_path_is_relative(std::string_view path)
@ -459,6 +334,13 @@ bool tr_sys_path_copy(char const* src_path, char const* dst_path, tr_error** err
#else /* USE_COPYFILE */
auto const info = tr_sys_path_get_info(src_path, 0, error);
if (!info)
{
tr_error_prefix(error, "Unable to get information on source file: ");
return false;
}
/* Other OSes require us to copy between file descriptors, so open them. */
tr_sys_file_t const in = tr_sys_file_open(src_path, TR_SYS_FILE_READ | TR_SYS_FILE_SEQUENTIAL, 0, error);
if (in == TR_BAD_SYS_FILE)
@ -467,14 +349,6 @@ bool tr_sys_path_copy(char const* src_path, char const* dst_path, tr_error** err
return false;
}
auto const info = tr_sys_file_get_info(in, error);
if (!info)
{
tr_error_prefix(error, "Unable to get information on source file: ");
tr_sys_file_close(in);
return false;
}
tr_sys_file_t const out = tr_sys_file_open(
dst_path,
TR_SYS_FILE_WRITE | TR_SYS_FILE_CREATE | TR_SYS_FILE_TRUNCATE,
@ -685,19 +559,6 @@ bool tr_sys_file_close(tr_sys_file_t handle, tr_error** error)
return ret;
}
std::optional<tr_sys_path_info> tr_sys_file_get_info(tr_sys_file_t handle, tr_error** error)
{
TR_ASSERT(handle != TR_BAD_SYS_FILE);
if (struct stat sb; fstat(handle, &sb) != -1)
{
return stat_to_sys_path_info(sb);
}
set_system_error(error, errno);
return {};
}
bool tr_sys_file_read(tr_sys_file_t handle, void* buffer, uint64_t size, uint64_t* bytes_read, tr_error** error)
{
TR_ASSERT(handle != TR_BAD_SYS_FILE);
@ -1143,6 +1004,40 @@ std::string tr_sys_dir_get_current(tr_error** error)
}
}
#ifndef HAVE_MKDIRP
static bool tr_mkdirp_(std::string_view path, int permissions, tr_error** error)
{
auto walk = path.find_first_not_of('/'); // walk past the root
auto subpath = tr_pathbuf{};
while (walk < std::size(path))
{
auto const end = path.find('/', walk);
subpath.assign(path.substr(0, end));
auto const info = tr_sys_path_get_info(subpath, 0);
if (info && !info->isFolder())
{
tr_error_set(error, ENOTDIR, fmt::format(FMT_STRING("File is in the way: {:s}"), path));
return false;
}
if (!info && mkdir(subpath, permissions) == -1)
{
set_system_error(error, errno);
return false;
}
if (end == std::string_view::npos)
{
break;
}
walk = end + 1;
}
return true;
}
#endif
bool tr_sys_dir_create(char const* path, int flags, int permissions, tr_error** error)
{
TR_ASSERT(path != nullptr);
@ -1155,7 +1050,7 @@ bool tr_sys_dir_create(char const* path, int flags, int permissions, tr_error**
#ifdef HAVE_MKDIRP
ret = mkdirp(path, permissions) != -1;
#else
ret = create_path(path, permissions, &my_error);
ret = tr_mkdirp_(path, permissions, &my_error);
#endif
}
else
@ -1165,9 +1060,7 @@ bool tr_sys_dir_create(char const* path, int flags, int permissions, tr_error**
if (!ret && errno == EEXIST)
{
struct stat sb;
if (stat(path, &sb) != -1 && S_ISDIR(sb.st_mode))
if (auto const info = tr_sys_path_get_info(path); info && info->type == TR_SYS_PATH_IS_DIRECTORY)
{
tr_error_clear(&my_error);
ret = true;

View File

@ -255,6 +255,12 @@ static bool create_dir(std::string_view path, int flags, int /*permissions*/, bo
DWORD error_code = ERROR_SUCCESS;
auto const wide_path = path_to_native_path(path);
// already exists (no-op)
if (auto const info = tr_sys_path_get_info(path); info && info->isFolder())
{
return true;
}
if ((flags & TR_SYS_DIR_CREATE_PARENTS) != 0)
{
error_code = SHCreateDirectoryExW(nullptr, wide_path.c_str(), nullptr);
@ -375,6 +381,24 @@ bool tr_sys_path_exists(char const* path, tr_error** error)
return ret;
}
static std::optional<tr_sys_path_info> tr_sys_file_get_info_(tr_sys_file_t handle, tr_error** error)
{
TR_ASSERT(handle != TR_BAD_SYS_FILE);
auto attributes = BY_HANDLE_FILE_INFORMATION{};
if (GetFileInformationByHandle(handle, &attributes))
{
return stat_to_sys_path_info(
attributes.dwFileAttributes,
attributes.nFileSizeLow,
attributes.nFileSizeHigh,
attributes.ftLastWriteTime);
}
set_system_error(error, GetLastError());
return {};
}
std::optional<tr_sys_path_info> tr_sys_path_get_info(std::string_view path, int flags, tr_error** error)
{
if (auto const wide_path = path_to_native_path(path); std::empty(wide_path))
@ -397,7 +421,7 @@ std::optional<tr_sys_path_info> tr_sys_path_get_info(std::string_view path, int
handle = CreateFileW(wide_path.c_str(), 0, 0, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
handle != INVALID_HANDLE_VALUE)
{
auto ret = tr_sys_file_get_info(handle, error);
auto ret = tr_sys_file_get_info_(handle, error);
CloseHandle(handle);
return ret;
}
@ -929,24 +953,6 @@ bool tr_sys_file_close(tr_sys_file_t handle, tr_error** error)
return ret;
}
std::optional<tr_sys_path_info> tr_sys_file_get_info(tr_sys_file_t handle, tr_error** error)
{
TR_ASSERT(handle != TR_BAD_SYS_FILE);
auto attributes = BY_HANDLE_FILE_INFORMATION{};
if (GetFileInformationByHandle(handle, &attributes))
{
return stat_to_sys_path_info(
attributes.dwFileAttributes,
attributes.nFileSizeLow,
attributes.nFileSizeHigh,
attributes.ftLastWriteTime);
}
set_system_error(error, GetLastError());
return {};
}
bool tr_sys_file_read(tr_sys_file_t handle, void* buffer, uint64_t size, uint64_t* bytes_read, tr_error** error)
{
TR_ASSERT(handle != TR_BAD_SYS_FILE);
@ -1248,16 +1254,17 @@ tr_sys_dir_t tr_sys_dir_open(char const* path, tr_error** error)
{
TR_ASSERT(path != nullptr);
#ifndef __clang__
/* Clang gives "static_assert expression is not an integral constant expression" error */
static_assert(TR_BAD_SYS_DIR == nullptr, "values should match");
#endif
if (auto const info = tr_sys_path_get_info(path, 0); !info || !info->isFolder())
{
set_system_error(error, ERROR_DIRECTORY);
return TR_BAD_SYS_DIR;
}
auto pattern = path_to_native_path(path);
if (std::empty(pattern))
{
set_system_error(error, GetLastError());
return nullptr;
return TR_BAD_SYS_DIR;
}
auto* const ret = new tr_sys_dir_win32{};

View File

@ -362,17 +362,6 @@ tr_sys_file_t tr_sys_file_open_temp(char* path_template, struct tr_error** error
*/
bool tr_sys_file_close(tr_sys_file_t handle, struct tr_error** error = nullptr);
/**
* @brief Portability wrapper for `fstat()`.
*
* @param[in] handle Valid file descriptor.
* @param[out] error Pointer to error object. Optional, pass `nullptr` if you
* are not interested in error details.
*
* @return info on success, or nullopt with `error` set accordingly.
*/
[[nodiscard]] std::optional<tr_sys_path_info> tr_sys_file_get_info(tr_sys_file_t handle, struct tr_error** error = nullptr);
/**
* @brief Portability wrapper for `read()`.
*

View File

@ -235,17 +235,6 @@ TEST_F(FileTest, getInfo)
EXPECT_GE(info->last_modified_at, t - 1);
EXPECT_LE(info->last_modified_at, time(nullptr) + 1);
// Good file info (by handle)
auto fd = tr_sys_file_open(path1, TR_SYS_FILE_READ, 0);
info = tr_sys_file_get_info(fd, &err);
EXPECT_TRUE(info);
EXPECT_EQ(nullptr, err) << *err;
EXPECT_EQ(TR_SYS_PATH_IS_FILE, info->type);
EXPECT_EQ(4, info->size);
EXPECT_GE(info->last_modified_at, t - 1);
EXPECT_LE(info->last_modified_at, time(nullptr) + 1);
tr_sys_file_close(fd);
tr_sys_path_remove(path1);
// Good directory info
@ -280,16 +269,11 @@ TEST_F(FileTest, getInfo)
EXPECT_GE(info->last_modified_at, t - 1);
EXPECT_LE(info->last_modified_at, time(nullptr) + 1);
// Good file info (by handle)
fd = tr_sys_file_open(path1, TR_SYS_FILE_READ, 0);
info = tr_sys_file_get_info(fd, &err);
// Symlink
info = tr_sys_path_get_info(path1, TR_SYS_PATH_NO_FOLLOW, &err);
EXPECT_TRUE(info);
EXPECT_EQ(nullptr, err) << *err;
EXPECT_EQ(TR_SYS_PATH_IS_FILE, info->type);
EXPECT_EQ(4, info->size);
EXPECT_GE(info->last_modified_at, t - 1);
EXPECT_LE(info->last_modified_at, time(nullptr) + 1);
tr_sys_file_close(fd);
EXPECT_EQ(TR_SYS_PATH_IS_OTHER, info->type);
tr_sys_path_remove(path2);
tr_sys_path_remove(path1);
@ -315,6 +299,50 @@ TEST_F(FileTest, getInfo)
}
}
TEST_F(FileTest, readFile)
{
auto const test_dir = createTestDir(currentTestName());
auto const path = tr_pathbuf{ test_dir, "/a.txt"sv };
auto constexpr Contents = "hello, world!"sv;
createFileWithContents(path, Contents);
auto n_bytes_read = uint64_t{};
tr_error* err = nullptr;
auto buf = std::array<char, 64>{};
auto fd = tr_sys_file_open(path, TR_SYS_FILE_READ, 0);
EXPECT_NE(TR_BAD_SYS_FILE, fd);
// successful read
EXPECT_TRUE(tr_sys_file_read(fd, std::data(buf), std::size(buf), &n_bytes_read, &err));
EXPECT_EQ(Contents, std::string_view(std::data(buf), n_bytes_read));
EXPECT_EQ(std::size(Contents), n_bytes_read);
EXPECT_EQ(nullptr, err) << *err;
// successful read_at
auto const offset = 1U;
EXPECT_TRUE(tr_sys_file_read_at(fd, std::data(buf), std::size(buf), offset, &n_bytes_read, &err));
auto constexpr Expected = Contents.substr(offset);
EXPECT_EQ(Expected, std::string_view(std::data(buf), n_bytes_read));
EXPECT_EQ(std::size(Expected), n_bytes_read);
EXPECT_EQ(nullptr, err) << *err;
tr_sys_file_close(fd);
// read from closed file
n_bytes_read = 0;
EXPECT_FALSE(tr_sys_file_read(fd, std::data(buf), std::size(buf), &n_bytes_read, &err));
EXPECT_EQ(0, n_bytes_read);
EXPECT_NE(nullptr, err);
tr_error_clear(&err);
// read_at from closed file
EXPECT_FALSE(tr_sys_file_read_at(fd, std::data(buf), std::size(buf), offset, &n_bytes_read, &err));
EXPECT_EQ(0, n_bytes_read);
EXPECT_NE(nullptr, err);
tr_error_clear(&err);
}
TEST_F(FileTest, pathExists)
{
auto const test_dir = createTestDir(currentTestName());
@ -1019,6 +1047,40 @@ TEST_F(FileTest, pathNativeSeparators)
}
}
TEST_F(FileTest, fileCopy)
{
auto const test_dir = createTestDir(currentTestName());
auto const path1 = tr_pathbuf{ test_dir, "/a.txt" };
auto const path2 = tr_pathbuf{ test_dir, "/b.txt" };
auto constexpr Contents = "hello, world!"sv;
// no source file
tr_error* err = nullptr;
EXPECT_FALSE(tr_sys_path_copy(path1, path2, &err));
EXPECT_NE(nullptr, err);
tr_error_clear(&err);
createFileWithContents(path1, Contents);
// source file exists but is inaccessible
chmod(path1, 0);
EXPECT_FALSE(tr_sys_path_copy(path1, test_dir, &err));
EXPECT_NE(nullptr, err);
tr_error_clear(&err);
chmod(path1, 0600);
// source file exists but target is invalid
EXPECT_FALSE(tr_sys_path_copy(path1, test_dir, &err));
EXPECT_NE(nullptr, err);
tr_error_clear(&err);
// source and target are valid
createFileWithContents(path1, Contents);
EXPECT_TRUE(tr_sys_path_copy(path1, path2, &err));
EXPECT_EQ(nullptr, err);
}
TEST_F(FileTest, fileOpen)
{
auto const test_dir = createTestDir(currentTestName());
@ -1089,7 +1151,7 @@ TEST_F(FileTest, fileOpen)
fd = tr_sys_file_open(path1, TR_SYS_FILE_WRITE | TR_SYS_FILE_TRUNCATE, 0600, &err);
EXPECT_NE(TR_BAD_SYS_FILE, fd);
EXPECT_EQ(nullptr, err) << *err;
info = tr_sys_file_get_info(fd);
info = tr_sys_path_get_info(path1);
EXPECT_TRUE(info);
EXPECT_EQ(0U, info->size);
tr_sys_file_close(fd);
@ -1106,25 +1168,25 @@ TEST_F(FileTest, fileTruncate)
{
auto const test_dir = createTestDir(currentTestName());
auto const path1 = tr_pathbuf{ test_dir, "/a"sv };
auto fd = tr_sys_file_open(path1, TR_SYS_FILE_WRITE | TR_SYS_FILE_CREATE, 0600);
auto const path = tr_pathbuf{ test_dir, "/a"sv };
auto fd = tr_sys_file_open(path, TR_SYS_FILE_WRITE | TR_SYS_FILE_CREATE, 0600);
tr_error* err = nullptr;
EXPECT_TRUE(tr_sys_file_truncate(fd, 10, &err));
EXPECT_EQ(nullptr, err) << *err;
auto info = tr_sys_file_get_info(fd);
auto info = tr_sys_path_get_info(path);
EXPECT_TRUE(info);
EXPECT_EQ(10U, info->size);
EXPECT_TRUE(tr_sys_file_truncate(fd, 20, &err));
EXPECT_EQ(nullptr, err) << *err;
info = tr_sys_file_get_info(fd);
info = tr_sys_path_get_info(path);
EXPECT_TRUE(info);
EXPECT_EQ(20U, info->size);
EXPECT_TRUE(tr_sys_file_truncate(fd, 0, &err));
EXPECT_EQ(nullptr, err) << *err;
info = tr_sys_file_get_info(fd);
info = tr_sys_path_get_info(path);
EXPECT_TRUE(info);
EXPECT_EQ(0U, info->size);
@ -1133,22 +1195,27 @@ TEST_F(FileTest, fileTruncate)
tr_sys_file_close(fd);
info = tr_sys_path_get_info(path1);
info = tr_sys_path_get_info(path);
EXPECT_TRUE(info);
EXPECT_EQ(50U, info->size);
fd = tr_sys_file_open(path1, TR_SYS_FILE_WRITE | TR_SYS_FILE_CREATE, 0600);
fd = tr_sys_file_open(path, TR_SYS_FILE_WRITE | TR_SYS_FILE_CREATE, 0600);
EXPECT_TRUE(tr_sys_file_truncate(fd, 25, &err));
EXPECT_EQ(nullptr, err) << *err;
tr_sys_file_close(fd);
info = tr_sys_path_get_info(path1);
info = tr_sys_path_get_info(path);
EXPECT_TRUE(info);
EXPECT_EQ(25U, info->size);
tr_sys_path_remove(path1);
// try to truncate a closed file
EXPECT_FALSE(tr_sys_file_truncate(fd, 10, &err));
EXPECT_NE(nullptr, err);
tr_error_clear(&err);
tr_sys_path_remove(path);
}
TEST_F(FileTest, filePreallocate)
@ -1163,7 +1230,7 @@ TEST_F(FileTest, filePreallocate)
if (tr_sys_file_preallocate(fd, prealloc_size, 0, &err))
{
EXPECT_EQ(nullptr, err) << *err;
auto info = tr_sys_file_get_info(fd);
auto info = tr_sys_path_get_info(path1);
EXPECT_TRUE(info);
EXPECT_EQ(prealloc_size, info->size);
}
@ -1184,7 +1251,7 @@ TEST_F(FileTest, filePreallocate)
if (tr_sys_file_preallocate(fd, prealloc_size, TR_SYS_FILE_PREALLOC_SPARSE, &err))
{
EXPECT_EQ(nullptr, err) << *err;
auto info = tr_sys_file_get_info(fd);
auto info = tr_sys_path_get_info(path1);
EXPECT_TRUE(info);
EXPECT_EQ(prealloc_size, info->size);
}
@ -1251,6 +1318,22 @@ TEST_F(FileTest, dirCreate)
tr_sys_path_remove(path1);
}
TEST_F(FileTest, dirCreateTemp)
{
auto const test_dir = createTestDir(currentTestName());
tr_error* err = nullptr;
auto path = tr_pathbuf{ test_dir, "/test-XXXXXX" };
EXPECT_TRUE(tr_sys_dir_create_temp(std::data(path), &err));
EXPECT_EQ(nullptr, err) << *err;
tr_sys_path_remove(path);
path.assign(test_dir, "/path-does-not-exist/test-XXXXXX");
EXPECT_FALSE(tr_sys_dir_create_temp(std::data(path), &err));
EXPECT_NE(nullptr, err);
tr_error_clear(&err);
}
TEST_F(FileTest, dirRead)
{
auto const test_dir = createTestDir(currentTestName());
@ -1280,6 +1363,42 @@ TEST_F(FileTest, dirRead)
EXPECT_TRUE(have2);
}
TEST_F(FileTest, dirOpen)
{
auto const test_dir = createTestDir(currentTestName());
auto const file = tr_pathbuf{ test_dir, "/foo.fxt" };
auto constexpr Contents = "hello, world!"sv;
createFileWithContents(file, std::data(Contents), std::size(Contents));
// path does not exist
tr_error* err = nullptr;
auto odir = tr_sys_dir_open("/no/such/path", &err);
EXPECT_EQ(TR_BAD_SYS_DIR, odir);
EXPECT_NE(err, nullptr);
tr_error_clear(&err);
// path is not a directory
odir = tr_sys_dir_open(file, &err);
EXPECT_EQ(TR_BAD_SYS_DIR, odir);
EXPECT_NE(err, nullptr);
tr_error_clear(&err);
// path exists and is readable
odir = tr_sys_dir_open(test_dir);
EXPECT_NE(TR_BAD_SYS_DIR, odir);
auto files = std::set<std::string>{};
char const* filename = nullptr;
while ((filename = tr_sys_dir_read_name(odir, &err)))
{
files.insert(filename);
}
EXPECT_EQ(3U, files.size());
EXPECT_EQ(nullptr, err) << *err;
EXPECT_TRUE(tr_sys_dir_close(odir, &err));
EXPECT_EQ(nullptr, err) << *err;
}
} // namespace test
} // namespace libtransmission

View File

@ -127,7 +127,6 @@ protected:
auto path = tr_sys_dir_get_current(&error);
if (error != nullptr)
{
std::cerr << "tr_sys_dir_get_current error: '" << error->message << "'" << std::endl;
tr_error_free(error);
}
return path;
@ -184,9 +183,13 @@ protected:
auto dir = tr_pathbuf{ path };
dir.popdir();
tr_error* error = nullptr;
tr_sys_dir_create(dir, TR_SYS_DIR_CREATE_PARENTS, 0700, &error);
EXPECT_EQ(nullptr, error) << "path[" << path << "] dir[" << dir << "] " << *error;
if (auto const info = tr_sys_path_get_info(path); !info)
{
tr_error* error = nullptr;
tr_sys_dir_create(dir, TR_SYS_DIR_CREATE_PARENTS, 0700, &error);
EXPECT_EQ(nullptr, error) << "path[" << path << "] dir[" << dir << "] " << *error;
tr_error_clear(&error);
}
errno = tmperr;
}