2018-12-31 20:04:05 +01:00
|
|
|
//
|
2018-12-31 23:02:34 +01:00
|
|
|
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019
|
2018-12-31 20:04:05 +01:00
|
|
|
//
|
|
|
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
|
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
|
|
//
|
|
|
|
#include "td/telegram/files/FileLoaderUtils.h"
|
|
|
|
|
|
|
|
#include "td/telegram/files/FileLocation.h"
|
|
|
|
#include "td/telegram/Global.h"
|
|
|
|
|
|
|
|
#include "td/utils/common.h"
|
|
|
|
#include "td/utils/filesystem.h"
|
|
|
|
#include "td/utils/format.h"
|
|
|
|
#include "td/utils/logging.h"
|
|
|
|
#include "td/utils/misc.h"
|
|
|
|
#include "td/utils/PathView.h"
|
|
|
|
#include "td/utils/port/FileFd.h"
|
|
|
|
#include "td/utils/port/path.h"
|
|
|
|
#include "td/utils/Random.h"
|
|
|
|
#include "td/utils/StringBuilder.h"
|
|
|
|
|
2018-02-28 11:25:07 +01:00
|
|
|
#include <tuple>
|
|
|
|
|
2018-12-31 20:04:05 +01:00
|
|
|
namespace td {
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
Result<std::pair<FileFd, string>> try_create_new_file(Result<CSlice> result_name) {
|
|
|
|
TRY_RESULT(name, std::move(result_name));
|
|
|
|
TRY_RESULT(fd, FileFd::open(name, FileFd::Read | FileFd::Write | FileFd::CreateNew, 0640));
|
|
|
|
return std::make_pair(std::move(fd), name.str());
|
|
|
|
}
|
2018-02-26 11:05:14 +01:00
|
|
|
Result<std::pair<FileFd, string>> try_open_file(Result<CSlice> result_name) {
|
|
|
|
TRY_RESULT(name, std::move(result_name));
|
|
|
|
TRY_RESULT(fd, FileFd::open(name, FileFd::Read, 0640));
|
|
|
|
return std::make_pair(std::move(fd), name.str());
|
|
|
|
}
|
2018-12-31 20:04:05 +01:00
|
|
|
|
|
|
|
struct RandSuff {
|
|
|
|
int len;
|
|
|
|
};
|
|
|
|
StringBuilder &operator<<(StringBuilder &sb, const RandSuff &) {
|
|
|
|
for (int i = 0; i < 6; i++) {
|
|
|
|
sb << format::hex_digit(Random::fast(0, 15));
|
|
|
|
}
|
|
|
|
return sb;
|
|
|
|
}
|
|
|
|
struct Ext {
|
|
|
|
Slice ext;
|
|
|
|
};
|
|
|
|
StringBuilder &operator<<(StringBuilder &sb, const Ext &ext) {
|
|
|
|
if (ext.ext.empty()) {
|
|
|
|
return sb;
|
|
|
|
}
|
|
|
|
return sb << "." << ext.ext;
|
|
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
2018-08-13 18:30:52 +02:00
|
|
|
Result<std::pair<FileFd, string>> open_temp_file(FileType file_type) {
|
2018-12-31 20:04:05 +01:00
|
|
|
auto pmc = G()->td_db()->get_binlog_pmc();
|
|
|
|
// TODO: CAS?
|
|
|
|
int32 file_id = to_integer<int32>(pmc->get("tmp_file_id"));
|
|
|
|
pmc->set("tmp_file_id", to_string(file_id + 1));
|
|
|
|
|
|
|
|
auto temp_dir = get_files_temp_dir(file_type);
|
|
|
|
auto res = try_create_new_file(PSLICE_SAFE() << temp_dir << file_id);
|
|
|
|
if (res.is_error()) {
|
|
|
|
res = try_create_new_file(PSLICE_SAFE() << temp_dir << file_id << "_" << RandSuff{6});
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2018-02-26 11:05:14 +01:00
|
|
|
template <class F>
|
|
|
|
bool for_suggested_file_name(CSlice name, bool use_pmc, bool use_random, F &&callback) {
|
|
|
|
auto try_callback = [&](Result<CSlice> r_path) {
|
|
|
|
if (r_path.is_error()) {
|
|
|
|
return true;
|
|
|
|
}
|
2018-12-27 23:48:21 +01:00
|
|
|
LOG(DEBUG) << "Trying " << r_path.ok();
|
2018-02-26 11:05:14 +01:00
|
|
|
return callback(r_path.move_as_ok());
|
|
|
|
};
|
2018-12-31 20:04:05 +01:00
|
|
|
auto cleaned_name = clean_filename(name);
|
|
|
|
PathView path_view(cleaned_name);
|
|
|
|
auto stem = path_view.file_stem();
|
|
|
|
auto ext = path_view.extension();
|
2018-02-26 11:05:14 +01:00
|
|
|
bool active = true;
|
2018-12-31 20:04:05 +01:00
|
|
|
if (!stem.empty() && !G()->parameters().ignore_file_names) {
|
2018-02-26 11:05:14 +01:00
|
|
|
active = try_callback(PSLICE_SAFE() << stem << Ext{ext});
|
|
|
|
for (int i = 0; active && i < 10; i++) {
|
|
|
|
active = try_callback(PSLICE_SAFE() << stem << "_(" << i << ")" << Ext{ext});
|
2018-12-31 20:04:05 +01:00
|
|
|
}
|
2018-02-26 11:05:14 +01:00
|
|
|
for (int i = 2; active && i < 12 && use_random; i++) {
|
|
|
|
active = try_callback(PSLICE_SAFE() << stem << "_(" << RandSuff{i} << ")" << Ext{ext});
|
2018-12-31 20:04:05 +01:00
|
|
|
}
|
2018-02-26 11:05:14 +01:00
|
|
|
} else if (use_pmc) {
|
2018-12-31 20:04:05 +01:00
|
|
|
auto pmc = G()->td_db()->get_binlog_pmc();
|
|
|
|
int32 file_id = to_integer<int32>(pmc->get("perm_file_id"));
|
|
|
|
pmc->set("perm_file_id", to_string(file_id + 1));
|
2018-02-26 11:05:14 +01:00
|
|
|
active = try_callback(PSLICE_SAFE() << "file_" << file_id << Ext{ext});
|
|
|
|
if (active) {
|
|
|
|
active = try_callback(PSLICE_SAFE() << "file_" << file_id << "_" << RandSuff{6} << Ext{ext});
|
2018-12-31 20:04:05 +01:00
|
|
|
}
|
|
|
|
}
|
2018-02-26 11:05:14 +01:00
|
|
|
return active;
|
|
|
|
}
|
2018-12-31 20:04:05 +01:00
|
|
|
|
2018-02-26 11:05:14 +01:00
|
|
|
Result<string> create_from_temp(CSlice temp_path, CSlice dir, CSlice name) {
|
|
|
|
LOG(INFO) << "Create file in directory " << dir << " with suggested name " << name << " from temporary file "
|
|
|
|
<< temp_path;
|
2018-02-28 11:25:07 +01:00
|
|
|
Result<std::pair<FileFd, string>> res = Status::Error(500, "Can't find suitable file name");
|
2018-02-26 11:05:14 +01:00
|
|
|
for_suggested_file_name(name, true, true, [&](CSlice suggested_name) {
|
|
|
|
res = try_create_new_file(PSLICE_SAFE() << dir << suggested_name);
|
|
|
|
return res.is_error();
|
|
|
|
});
|
2018-12-31 20:04:05 +01:00
|
|
|
TRY_RESULT(tmp, std::move(res));
|
|
|
|
tmp.first.close();
|
|
|
|
auto perm_path = std::move(tmp.second);
|
|
|
|
TRY_STATUS(rename(temp_path, perm_path));
|
|
|
|
return perm_path;
|
|
|
|
}
|
|
|
|
|
2018-02-26 11:05:14 +01:00
|
|
|
Result<string> search_file(CSlice dir, CSlice name, int64 expected_size) {
|
2018-02-28 11:25:07 +01:00
|
|
|
Result<std::string> res = Status::Error(500, "Can't find suitable file name");
|
2018-02-26 11:05:14 +01:00
|
|
|
for_suggested_file_name(name, false, false, [&](CSlice suggested_name) {
|
|
|
|
auto r_pair = try_open_file(PSLICE_SAFE() << dir << suggested_name);
|
|
|
|
if (r_pair.is_error()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
FileFd fd;
|
|
|
|
std::string path;
|
|
|
|
std::tie(fd, path) = r_pair.move_as_ok();
|
|
|
|
if (fd.stat().size_ != expected_size) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
fd.close();
|
|
|
|
res = std::move(path);
|
|
|
|
return false;
|
|
|
|
});
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2018-08-13 18:30:52 +02:00
|
|
|
Result<FullLocalFileLocation> save_file_bytes(FileType type, BufferSlice bytes, CSlice file_name) {
|
|
|
|
TRY_RESULT(fd_path, open_temp_file(type));
|
|
|
|
FileFd fd = std::move(fd_path.first);
|
|
|
|
string path = std::move(fd_path.second);
|
|
|
|
|
|
|
|
TRY_RESULT(size, fd.write(bytes.as_slice()));
|
|
|
|
fd.close();
|
|
|
|
|
|
|
|
if (size != bytes.size()) {
|
|
|
|
return Status::Error("Failed to write bytes to the file");
|
|
|
|
}
|
|
|
|
|
|
|
|
auto dir = get_files_dir(type);
|
|
|
|
TRY_RESULT(perm_path, create_from_temp(path, dir, file_name));
|
|
|
|
|
|
|
|
return FullLocalFileLocation(type, std::move(perm_path), 0);
|
|
|
|
}
|
|
|
|
|
2018-04-27 10:00:56 +02:00
|
|
|
const char *file_type_name[file_type_size] = {"thumbnails", "profile_photos", "photos", "voice",
|
|
|
|
"videos", "documents", "secret", "temp",
|
|
|
|
"stickers", "music", "animations", "secret_thumbnails",
|
|
|
|
"wallpapers", "video_notes", "passport", "passport"};
|
2018-12-31 20:04:05 +01:00
|
|
|
|
|
|
|
string get_file_base_dir(const FileDirType &file_dir_type) {
|
|
|
|
switch (file_dir_type) {
|
|
|
|
case FileDirType::Secure:
|
|
|
|
return G()->get_dir().str();
|
|
|
|
case FileDirType::Common:
|
|
|
|
return G()->get_files_dir().str();
|
|
|
|
default:
|
|
|
|
UNREACHABLE();
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-13 18:30:52 +02:00
|
|
|
string get_files_base_dir(FileType file_type) {
|
2018-12-31 20:04:05 +01:00
|
|
|
return get_file_base_dir(get_file_dir_type(file_type));
|
|
|
|
}
|
2018-08-13 18:30:52 +02:00
|
|
|
string get_files_temp_dir(FileType file_type) {
|
2018-12-31 20:04:05 +01:00
|
|
|
return get_files_base_dir(file_type) + "temp" + TD_DIR_SLASH;
|
|
|
|
}
|
2018-08-13 18:30:52 +02:00
|
|
|
string get_files_dir(FileType file_type) {
|
2018-12-31 20:04:05 +01:00
|
|
|
return get_files_base_dir(file_type) + file_type_name[static_cast<int32>(file_type)] + TD_DIR_SLASH;
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace td
|