extract common behavior

This commit is contained in:
2023-11-12 11:45:54 -06:00
parent db009b69dd
commit 72314606f3
6 changed files with 753 additions and 1170 deletions

View File

@@ -25,6 +25,7 @@
#include "comm/i_http_comm.hpp"
#include "events/events.hpp"
#include "file_manager/i_file_manager.hpp"
#include "providers/base_provider.hpp"
#include "types/repertory.hpp"
#include "utils/error_utils.hpp"
#include "utils/file_utils.hpp"
@@ -36,7 +37,7 @@
namespace repertory {
sia_provider::sia_provider(app_config &config, i_http_comm &comm)
: config_(config), comm_(comm) {}
: base_provider(config, comm) {}
auto sia_provider::get_object_info(const std::string &api_path,
json &object_info) const -> api_error {
@@ -54,7 +55,7 @@ auto sia_provider::get_object_info(const std::string &api_path,
long response_code{};
stop_type stop_requested{};
if (not comm_.make_request(get, response_code, stop_requested)) {
if (not get_comm().make_request(get, response_code, stop_requested)) {
return api_error::comm_error;
}
@@ -92,7 +93,7 @@ auto sia_provider::get_object_list(const std::string &api_path,
long response_code{};
stop_type stop_requested{};
if (not comm_.make_request(get, response_code, stop_requested)) {
if (not get_comm().make_request(get, response_code, stop_requested)) {
utils::error::raise_api_path_error(__FUNCTION__, api_path,
api_error::comm_error,
"failed to get object list");
@@ -108,37 +109,6 @@ auto sia_provider::get_object_list(const std::string &api_path,
return true;
}
auto sia_provider::create_api_file(std::string path, std::uint64_t size)
-> api_file {
api_file file{};
file.api_path = utils::path::create_api_path(path);
file.api_parent = utils::path::get_parent_api_path(file.api_path);
file.accessed_date = utils::get_file_time_now();
file.changed_date = utils::get_file_time_now();
file.creation_date = utils::get_file_time_now();
file.modified_date = utils::get_file_time_now();
file.file_size = size;
return file;
}
auto sia_provider::create_api_file(std::string path, std::uint64_t size,
api_meta_map &meta) -> api_file {
auto current_size = utils::string::to_uint64(meta[META_SIZE]);
if (current_size == 0U) {
current_size = size;
}
api_file file{};
file.api_path = utils::path::create_api_path(path);
file.api_parent = utils::path::get_parent_api_path(file.api_path);
file.accessed_date = utils::string::to_uint64(meta[META_ACCESSED]);
file.changed_date = utils::string::to_uint64(meta[META_CHANGED]);
file.creation_date = utils::string::to_uint64(meta[META_CREATION]);
file.file_size = current_size;
file.modified_date = utils::string::to_uint64(meta[META_MODIFIED]);
return file;
}
auto sia_provider::create_directory(const std::string &api_path,
api_meta_map &meta) -> api_error {
bool exists{};
@@ -170,7 +140,7 @@ auto sia_provider::create_directory(const std::string &api_path,
long response_code{};
stop_type stop_requested{};
if (not comm_.make_request(put_file, response_code, stop_requested)) {
if (not get_comm().make_request(put_file, response_code, stop_requested)) {
utils::error::raise_api_path_error(__FUNCTION__, api_path,
api_error::comm_error,
"failed to create directory");
@@ -192,57 +162,6 @@ auto sia_provider::create_directory(const std::string &api_path,
return set_item_meta(api_path, meta);
}
auto sia_provider::create_directory_clone_source_meta(
const std::string &source_api_path, const std::string &api_path)
-> api_error {
bool exists{};
auto res = is_file(source_api_path, exists);
if (res != api_error::success) {
return res;
}
if (exists) {
utils::error::raise_api_path_error(__FUNCTION__, api_path,
api_error::item_exists,
"failed to create directory");
return api_error::item_exists;
}
res = is_directory(api_path, exists);
if (res != api_error::success) {
return res;
}
if (exists) {
utils::error::raise_api_path_error(__FUNCTION__, api_path,
api_error::directory_exists,
"failed to create directory");
return api_error::directory_exists;
}
res = is_file(api_path, exists);
if (res != api_error::success) {
return res;
}
if (exists) {
utils::error::raise_api_path_error(__FUNCTION__, api_path,
api_error::item_exists,
"failed to create directory");
return api_error::item_exists;
}
api_meta_map meta{};
res = get_item_meta(source_api_path, meta);
if (res != api_error::success) {
if (res == api_error::item_not_found) {
res = api_error::directory_not_found;
}
utils::error::raise_api_path_error(__FUNCTION__, api_path, res,
"failed to create directory");
return res;
}
return create_directory(api_path, meta);
}
auto sia_provider::create_file(const std::string &api_path, api_meta_map &meta)
-> api_error {
bool exists{};
@@ -274,7 +193,7 @@ auto sia_provider::create_file(const std::string &api_path, api_meta_map &meta)
long response_code{};
stop_type stop_requested{};
if (not comm_.make_request(put_file, response_code, stop_requested)) {
if (not get_comm().make_request(put_file, response_code, stop_requested)) {
utils::error::raise_api_path_error(__FUNCTION__, api_path,
api_error::comm_error,
"failed to create file");
@@ -296,38 +215,6 @@ auto sia_provider::create_file(const std::string &api_path, api_meta_map &meta)
return set_item_meta(api_path, meta);
}
auto sia_provider::get_api_path_from_source(const std::string &source_path,
std::string &api_path) const
-> api_error {
if (source_path.empty()) {
utils::error::raise_api_path_error(__FUNCTION__, api_path,
api_error::item_not_found,
"failed to source path from api path");
return api_error::item_not_found;
}
auto iterator = std::unique_ptr<rocksdb::Iterator>(
db_->NewIterator(rocksdb::ReadOptions()));
for (iterator->SeekToFirst(); iterator->Valid(); iterator->Next()) {
std::string current_source_path{};
if (get_item_meta(iterator->key().ToString(), META_SOURCE,
current_source_path) != api_error::success) {
continue;
}
if (current_source_path.empty()) {
continue;
}
if (current_source_path == source_path) {
api_path = iterator->key().ToString();
return api_error::success;
}
}
return api_error::item_not_found;
}
auto sia_provider::get_directory_item_count(const std::string &api_path) const
-> std::uint64_t {
try {
@@ -399,9 +286,9 @@ auto sia_provider::get_directory_items(const std::string &api_path,
if (get_item_meta(entry_api_path, meta) ==
api_error::item_not_found) {
file = create_api_file(
entry_api_path,
entry_api_path, "",
directory ? 0U : entry["size"].get<std::uint64_t>());
api_item_added_(directory, file);
get_api_item_added()(directory, file);
res = get_item_meta(entry_api_path, meta);
if (res != api_error::success) {
utils::error::raise_error(__FUNCTION__, res,
@@ -414,14 +301,14 @@ auto sia_provider::get_directory_items(const std::string &api_path,
directory ? 0U : entry["size"].get<std::uint64_t>(), meta);
}
directory_item di{};
di.api_parent = file.api_parent;
di.api_path = file.api_path;
di.directory = directory;
di.meta = meta;
di.resolved = true;
di.size = file.file_size;
list.emplace_back(std::move(di));
directory_item dir_item{};
dir_item.api_parent = file.api_parent;
dir_item.api_path = file.api_path;
dir_item.directory = directory;
dir_item.meta = meta;
dir_item.resolved = true;
dir_item.size = file.file_size;
list.emplace_back(std::move(dir_item));
} catch (const std::exception &e) {
utils::error::raise_api_path_error(__FUNCTION__, api_path, e,
"failed to process entry|" +
@@ -472,8 +359,8 @@ auto sia_provider::get_file(const std::string &api_path, api_file &file) const
api_meta_map meta{};
if (get_item_meta(api_path, meta) == api_error::item_not_found) {
file = create_api_file(api_path, size);
api_item_added_(false, file);
file = create_api_file(api_path, "", size);
get_api_item_added()(false, file);
} else {
file = create_api_file(api_path, size, meta);
}
@@ -503,8 +390,8 @@ auto sia_provider::get_file_list(api_file_list &list) const -> api_error {
api_meta_map meta{};
if (get_item_meta(entry_api_path, meta) ==
api_error::item_not_found) {
auto dir = create_api_file(entry_api_path, 0U);
api_item_added_(true, dir);
auto dir = create_api_file(entry_api_path, "", 0U);
get_api_item_added()(true, dir);
}
auto res = get_files_in_dir(entry_api_path);
@@ -518,9 +405,9 @@ auto sia_provider::get_file_list(api_file_list &list) const -> api_error {
api_meta_map meta{};
if (get_item_meta(entry_api_path, meta) ==
api_error::item_not_found) {
file = create_api_file(entry_api_path,
file = create_api_file(entry_api_path, "",
entry["size"].get<std::uint64_t>());
api_item_added_(false, file);
get_api_item_added()(false, file);
} else {
file = create_api_file(entry_api_path,
entry["size"].get<std::uint64_t>(), meta);
@@ -563,146 +450,6 @@ auto sia_provider::get_file_size(const std::string &api_path,
return api_error::success;
}
auto sia_provider::get_filesystem_item(const std::string &api_path,
bool directory,
filesystem_item &fsi) const
-> api_error {
bool exists{};
auto res = is_directory(api_path, exists);
if (res != api_error::success) {
return res;
}
if (directory && not exists) {
return api_error::directory_not_found;
}
res = is_file(api_path, exists);
if (res != api_error::success) {
return res;
}
if (not directory && not exists) {
return api_error::item_not_found;
}
api_meta_map meta{};
res = get_item_meta(api_path, meta);
if (res != api_error::success) {
return res;
}
fsi.api_parent = utils::path::get_parent_api_path(api_path);
fsi.api_path = api_path;
fsi.directory = directory;
fsi.size = fsi.directory ? 0U : utils::string::to_uint64(meta[META_SIZE]);
fsi.source_path = meta[META_SOURCE];
return api_error::success;
}
auto sia_provider::get_filesystem_item_and_file(const std::string &api_path,
api_file &file,
filesystem_item &fsi) const
-> api_error {
auto res = get_file(api_path, file);
if (res != api_error::success) {
return res;
}
api_meta_map meta{};
res = get_item_meta(api_path, meta);
if (res != api_error::success) {
return res;
}
fsi.api_parent = utils::path::get_parent_api_path(api_path);
fsi.api_path = api_path;
fsi.directory = false;
fsi.size = utils::string::to_uint64(meta[META_SIZE]);
fsi.source_path = meta[META_SOURCE];
return api_error::success;
}
auto sia_provider::get_filesystem_item_from_source_path(
const std::string &source_path, filesystem_item &fsi) const -> api_error {
std::string api_path{};
auto res = get_api_path_from_source(source_path, api_path);
if (res != api_error::success) {
return res;
}
bool exists{};
res = is_directory(api_path, exists);
if (res != api_error::success) {
return res;
}
if (exists) {
return api_error::directory_exists;
}
return get_filesystem_item(api_path, false, fsi);
}
auto sia_provider::get_item_meta(const std::string &api_path,
api_meta_map &meta) const -> api_error {
std::string meta_value{};
db_->Get(rocksdb::ReadOptions(), api_path, &meta_value);
if (meta_value.empty()) {
return api_error::item_not_found;
}
try {
meta = json::parse(meta_value).get<api_meta_map>();
return api_error::success;
} catch (const std::exception &e) {
utils::error::raise_api_path_error(__FUNCTION__, api_path, e,
"failed to get item meta");
}
return api_error::error;
}
auto sia_provider::get_item_meta(const std::string &api_path,
const std::string &key,
std::string &value) const -> api_error {
std::string meta_value{};
db_->Get(rocksdb::ReadOptions(), api_path, &meta_value);
if (meta_value.empty()) {
return api_error::item_not_found;
}
try {
value = json::parse(meta_value)[key];
return api_error::success;
} catch (const std::exception &e) {
utils::error::raise_api_path_error(__FUNCTION__, api_path, e,
"failed to get item meta");
}
return api_error::error;
}
auto sia_provider::get_pinned_files() const -> std::vector<std::string> {
std::vector<std::string> ret{};
auto iterator = std::unique_ptr<rocksdb::Iterator>(
db_->NewIterator(rocksdb::ReadOptions()));
for (iterator->SeekToFirst(); iterator->Valid(); iterator->Next()) {
std::string pinned{};
if (get_item_meta(iterator->key().ToString(), META_PINNED, pinned) !=
api_error::success) {
continue;
}
if (pinned.empty() || not utils::string::to_bool(pinned)) {
continue;
}
ret.emplace_back(iterator->key().ToString());
}
return ret;
}
auto sia_provider::get_total_drive_space() const -> std::uint64_t {
try {
curl::requests::http_get get{};
@@ -719,7 +466,7 @@ auto sia_provider::get_total_drive_space() const -> std::uint64_t {
long response_code{};
stop_type stop_requested{};
if (not comm_.make_request(get, response_code, stop_requested)) {
if (not get_comm().make_request(get, response_code, stop_requested)) {
return 0U;
}
@@ -738,16 +485,6 @@ auto sia_provider::get_total_drive_space() const -> std::uint64_t {
return 0U;
}
auto sia_provider::get_total_item_count() const -> std::uint64_t {
std::uint64_t ret{};
auto iterator = std::unique_ptr<rocksdb::Iterator>(
db_->NewIterator(rocksdb::ReadOptions()));
for (iterator->SeekToFirst(); iterator->Valid(); iterator->Next()) {
ret++;
}
return ret;
}
auto sia_provider::get_used_drive_space() const -> std::uint64_t {
// TODO adjust size based on open files
try {
@@ -765,7 +502,7 @@ auto sia_provider::get_used_drive_space() const -> std::uint64_t {
long response_code{};
stop_type stop_requested{};
if (not comm_.make_request(get, response_code, stop_requested)) {
if (not get_comm().make_request(get, response_code, stop_requested)) {
return 0U;
}
@@ -776,7 +513,7 @@ auto sia_provider::get_used_drive_space() const -> std::uint64_t {
}
auto used_space = object_data["totalObjectsSize"].get<std::uint64_t>();
fm_->update_used_space(used_space);
get_file_mgr()->update_used_space(used_space);
return used_space;
} catch (const std::exception &ex) {
utils::error::raise_error(__FUNCTION__, ex,
@@ -845,17 +582,6 @@ auto sia_provider::is_file(const std::string &api_path, bool &exists) const
return api_error::error;
}
auto sia_provider::is_file_writeable(const std::string &api_path) const
-> bool {
bool exists{};
auto res = is_directory(api_path, exists);
if (res != api_error::success) {
return false;
}
return not exists;
}
auto sia_provider::is_online() const -> bool {
try {
curl::requests::http_get get{};
@@ -872,7 +598,7 @@ auto sia_provider::is_online() const -> bool {
long response_code{};
stop_type stop_requested{};
if (not comm_.make_request(get, response_code, stop_requested)) {
if (not get_comm().make_request(get, response_code, stop_requested)) {
utils::error::raise_error(__FUNCTION__, api_error::comm_error,
"failed to determine if provider is online");
return false;
@@ -910,7 +636,7 @@ auto sia_provider::read_file_bytes(const std::string &api_path,
auto res = api_error::comm_error;
for (std::uint32_t i = 0U; not stop_requested && res != api_error::success &&
i < config_.get_retry_read_count() + 1U;
i < get_config().get_retry_read_count() + 1U;
i++) {
long response_code{};
const auto notify_retry = [&]() {
@@ -930,7 +656,7 @@ auto sia_provider::read_file_bytes(const std::string &api_path,
std::this_thread::sleep_for(1s);
};
if (not comm_.make_request(get, response_code, stop_requested)) {
if (not get_comm().make_request(get, response_code, stop_requested)) {
notify_retry();
continue;
}
@@ -947,98 +673,6 @@ auto sia_provider::read_file_bytes(const std::string &api_path,
return res;
}
void sia_provider::remove_deleted_files() {
struct removed_item {
std::string api_path{};
bool directory{};
std::string source_path{};
};
api_file_list list{};
auto res = get_file_list(list);
if (res != api_error::success) {
utils::error::raise_error(__FUNCTION__, res, "failed to get file list");
return;
}
std::vector<removed_item> removed_list{};
auto iterator = std::unique_ptr<rocksdb::Iterator>(
db_->NewIterator(rocksdb::ReadOptions()));
for (iterator->SeekToFirst(); iterator->Valid(); iterator->Next()) {
api_meta_map meta{};
if (get_item_meta(iterator->key().ToString(), meta) == api_error::success) {
if (utils::string::to_bool(meta[META_DIRECTORY])) {
bool exists{};
if (is_directory(iterator->key().ToString(), exists) !=
api_error::success) {
continue;
}
if (not exists) {
removed_list.emplace_back(
removed_item{iterator->key().ToString(), true, ""});
}
continue;
}
bool exists{};
if (is_file(iterator->key().ToString(), exists) != api_error::success) {
continue;
}
if (not exists) {
removed_list.emplace_back(
removed_item{iterator->key().ToString(), false, meta[META_SOURCE]});
}
}
}
for (const auto &item : removed_list) {
if (not item.directory) {
if (utils::file::is_file(item.source_path)) {
const auto orphaned_directory =
utils::path::combine(config_.get_data_directory(), {"orphaned"});
if (utils::file::create_full_directory_path(orphaned_directory)) {
const auto parts = utils::string::split(item.api_path, '/', false);
const auto orphaned_file = utils::path::combine(
orphaned_directory,
{utils::path::strip_to_file_name(item.source_path) + '_' +
parts[parts.size() - 1U]});
event_system::instance().raise<orphaned_file_detected>(
item.source_path);
if (utils::file::reset_modified_time(item.source_path) &&
utils::file::copy_file(item.source_path, orphaned_file)) {
event_system::instance().raise<orphaned_file_processed>(
item.source_path, orphaned_file);
} else {
event_system::instance().raise<orphaned_file_processing_failed>(
item.source_path, orphaned_file,
std::to_string(utils::get_last_error_code()));
}
} else {
utils::error::raise_error(
__FUNCTION__, std::to_string(utils::get_last_error_code()),
"failed to create orphaned director|sp|" + orphaned_directory);
continue;
}
}
if (fm_->evict_file(item.api_path)) {
db_->Delete(rocksdb::WriteOptions(), item.api_path);
event_system::instance().raise<file_removed_externally>(
item.api_path, item.source_path);
}
}
}
for (const auto &item : removed_list) {
if (item.directory) {
db_->Delete(rocksdb::WriteOptions(), item.api_path);
event_system::instance().raise<directory_removed_externally>(
item.api_path, item.source_path);
}
}
}
auto sia_provider::remove_directory(const std::string &api_path) -> api_error {
const auto notify_end = [&api_path](api_error error) -> api_error {
if (error == api_error::success) {
@@ -1065,7 +699,7 @@ auto sia_provider::remove_directory(const std::string &api_path) -> api_error {
long response_code{};
stop_type stop_requested{};
if (not comm_.make_request(del, response_code, stop_requested)) {
if (not get_comm().make_request(del, response_code, stop_requested)) {
utils::error::raise_api_path_error(__FUNCTION__, api_path,
api_error::comm_error,
"failed to remove directory");
@@ -1078,7 +712,7 @@ auto sia_provider::remove_directory(const std::string &api_path) -> api_error {
return notify_end(api_error::comm_error);
}
auto res2 = db_->Delete(rocksdb::WriteOptions(), api_path);
auto res2 = get_db()->Delete(rocksdb::WriteOptions(), api_path);
if (not res2.ok()) {
utils::error::raise_api_path_error(__FUNCTION__, api_path, res2.code(),
"failed to remove directory");
@@ -1104,7 +738,7 @@ auto sia_provider::remove_file(const std::string &api_path) -> api_error {
api_meta_map meta{};
auto res = get_item_meta(api_path, meta);
auto res2 = db_->Delete(rocksdb::WriteOptions(), api_path);
auto res2 = get_db()->Delete(rocksdb::WriteOptions(), api_path);
if (not res2.ok()) {
utils::error::raise_api_path_error(__FUNCTION__, api_path, res2.code(),
"failed to remove file");
@@ -1140,7 +774,7 @@ auto sia_provider::remove_file(const std::string &api_path) -> api_error {
long response_code{};
stop_type stop_requested{};
if (not comm_.make_request(del, response_code, stop_requested)) {
if (not get_comm().make_request(del, response_code, stop_requested)) {
utils::error::raise_api_path_error(
__FUNCTION__, api_path, api_error::comm_error, "failed to remove file");
return notify_end(api_error::comm_error);
@@ -1156,139 +790,19 @@ auto sia_provider::remove_file(const std::string &api_path) -> api_error {
return remove_file_meta();
}
auto sia_provider::remove_item_meta(const std::string &api_path,
const std::string &key) -> api_error {
api_meta_map meta{};
auto res = get_item_meta(api_path, meta);
if (res != api_error::success) {
return res;
}
meta.erase(key);
auto res2 = db_->Put(rocksdb::WriteOptions(), api_path, json(meta).dump());
if (not res2.ok()) {
utils::error::raise_api_path_error(__FUNCTION__, api_path, res2.code(),
"failed to remove item meta");
return api_error::error;
}
return api_error::success;
}
auto sia_provider::rename_file(const std::string & /*from_api_path*/,
const std::string & /*to_api_path*/)
-> api_error {
return api_error::not_implemented;
}
auto sia_provider::set_item_meta(const std::string &api_path,
const std::string &key,
const std::string &value) -> api_error {
json meta_json{};
std::string meta_value{};
db_->Get(rocksdb::ReadOptions(), api_path, &meta_value);
if (not meta_value.empty()) {
try {
meta_json = json::parse(meta_value);
} catch (const std::exception &e) {
utils::error::raise_api_path_error(__FUNCTION__, api_path, e,
"failed to set item meta");
return api_error::error;
}
}
meta_json[key] = value;
const auto res =
db_->Put(rocksdb::WriteOptions(), api_path, meta_json.dump());
if (not res.ok()) {
utils::error::raise_api_path_error(__FUNCTION__, api_path, res.code(),
"failed to set item meta");
return api_error::error;
}
return api_error::success;
}
auto sia_provider::set_item_meta(const std::string &api_path,
const api_meta_map &meta) -> api_error {
json meta_json{};
std::string meta_value{};
db_->Get(rocksdb::ReadOptions(), api_path, &meta_value);
if (not meta_value.empty()) {
try {
meta_json = json::parse(meta_value);
} catch (const std::exception &e) {
utils::error::raise_api_path_error(__FUNCTION__, api_path, e,
"failed to set item meta");
return api_error::error;
}
}
for (const auto &kv : meta) {
meta_json[kv.first] = kv.second;
}
const auto res =
db_->Put(rocksdb::WriteOptions(), api_path, meta_json.dump());
if (not res.ok()) {
utils::error::raise_api_path_error(__FUNCTION__, api_path, res.code(),
"failed to set item meta");
return api_error::error;
}
return api_error::success;
}
auto sia_provider::start(api_item_added_callback api_item_added,
i_file_manager *mgr) -> bool {
event_system::instance().raise<service_started>("sia_provider");
utils::db::create_rocksdb(config_, DB_NAME, db_);
api_item_added_ = api_item_added;
fm_ = mgr;
api_meta_map meta{};
if (get_item_meta("/", meta) == api_error::item_not_found) {
auto dir = create_api_file("/", 0U);
api_item_added_(true, dir);
}
auto online = false;
auto unmount_requested = false;
{
repertory::event_consumer ec(
"unmount_requested",
[&unmount_requested](const event &) { unmount_requested = true; });
for (std::uint16_t i = 0u; not online && not unmount_requested &&
(i < config_.get_online_check_retry_secs());
i++) {
online = is_online();
if (not online) {
event_system::instance().raise<provider_offline>(
config_.get_host_config().host_name_or_ip,
config_.get_host_config().api_port);
std::this_thread::sleep_for(1s);
}
}
}
if (online && not unmount_requested) {
polling::instance().set_callback({"check_deleted", polling::frequency::low,
[this]() { remove_deleted_files(); }});
return true;
}
return false;
return base_provider::start(api_item_added, mgr);
}
void sia_provider::stop() {
event_system::instance().raise<service_shutdown_begin>("sia_provider");
polling::instance().remove_callback("check_deleted");
db_.reset();
event_system::instance().raise<service_shutdown_end>("sia_provider");
}
void sia_provider::stop() { return base_provider::stop(); }
auto sia_provider::upload_file(const std::string &api_path,
const std::string &source_path,
@@ -1308,7 +822,7 @@ auto sia_provider::upload_file(const std::string &api_path,
put_file.source_path = source_path;
long response_code{};
if (not comm_.make_request(put_file, response_code, stop_requested)) {
if (not get_comm().make_request(put_file, response_code, stop_requested)) {
utils::error::raise_api_path_error(__FUNCTION__, api_path, source_path,
api_error::comm_error,
"failed to upload file");