📄 tarstream.cc
字号:
ASSERT(NULL != error); ASSERT(M_WRITE == mode_); ASSERT(NB_FILE_HEADER == next_block_); ASSERT(BLOCK_SIZE == block_pos_); ASSERT(NULL == current_); ASSERT(0 == current_bytes_); std::string pathname, link, linked_name, magic, mversion; size_t file_size, modify_time, unused, checksum; size_t block_data = 0; ReadFieldS(block_data, 100, &pathname); ReadFieldN(block_data, 8, &unused); // mode ReadFieldN(block_data, 8, &unused); // owner uid ReadFieldN(block_data, 8, &unused); // owner gid ReadFieldN(block_data, 12, &file_size); ReadFieldN(block_data, 12, &modify_time); ReadFieldN(block_data, 8, &checksum); if (checksum == 0) block_data -= 8; // back-compatiblity ReadFieldS(block_data, 1, &link); ReadFieldS(block_data, 100, &linked_name); // name of linked file ReadFieldS(block_data, 6, &magic); ReadFieldS(block_data, 2, &mversion); if (pathname.empty()) return SR_EOS; std::string user, group, dev_major, dev_minor, prefix; if (magic == "ustar" || magic == "ustar ") { ReadFieldS(block_data, 32, &user); ReadFieldS(block_data, 32, &group); ReadFieldS(block_data, 8, &dev_major); ReadFieldS(block_data, 8, &dev_minor); ReadFieldS(block_data, 155, &prefix); pathname = prefix + pathname; } // Rest of the block must be empty StreamResult result = ProcessEmptyBlock(block_data, error); if (SR_SUCCESS != result) { return result; } Pathname archive_path(pathname); archive_path.SetFolderDelimiter('/'); archive_path.Normalize(); bool is_folder = archive_path.filename().empty(); if (is_folder) { ASSERT(NB_FILE_HEADER == next_block_); ASSERT(0 == file_size); } else if (file_size > 0) { // We assign current_bytes_ because we must skip over the upcoming data // segments, regardless of whether we want to write them. next_block_ = NB_DATA; current_bytes_ = file_size; } if (!CheckFilter(archive_path.pathname())) { // If it's a directory, we will ignore it and all children by nature of // filter prefix matching. If it is a file, we will ignore it because // current_ is NULL. return SR_SUCCESS; } // Sanity checks: // 1) No .. path segments if (archive_path.pathname().find("../") != std::string::npos) { LOG_F(LS_WARNING) << "Skipping path with .. entry: " << archive_path.pathname(); return SR_SUCCESS; } // 2) No drive letters if (archive_path.pathname().find(':') != std::string::npos) { LOG_F(LS_WARNING) << "Skipping path with drive letter: " << archive_path.pathname(); return SR_SUCCESS; } // 3) No absolute paths if (archive_path.pathname().find("//") != std::string::npos) { LOG_F(LS_WARNING) << "Skipping absolute path: " << archive_path.pathname(); return SR_SUCCESS; } Pathname local_path(root_folder_); local_path.AppendPathname(archive_path.pathname()); local_path.Normalize(); if (is_folder) { if (!Filesystem::CreateFolder(local_path)) { LOG_F(LS_WARNING) << "Couldn't create folder: " << local_path.pathname(); *error = 0; // TODO return SR_ERROR; } } else { FileStream* stream = new FileStream; if (!stream->Open(local_path.pathname().c_str(), "wb")) { LOG_F(LS_WARNING) << "Couldn't create file: " << local_path.pathname(); *error = 0; // TODO delete stream; return SR_ERROR; } if (file_size > 0) { current_ = stream; } else { stream->Close(); delete stream; } } SignalNextEntry(archive_path.filename(), current_bytes_); return SR_SUCCESS;}StreamResult TarStream::ProcessNextEntry(const DirectoryIterator *data, int *error) { ASSERT(M_READ == mode_); ASSERT(NB_FILE_HEADER == next_block_); ASSERT(BLOCK_SIZE == block_pos_); ASSERT(NULL == current_); ASSERT(0 == current_bytes_); if (data->IsDirectory() && (data->Name() == "." || data->Name() == "..")) return SR_SUCCESS; Pathname archive_path; archive_path.SetFolder(subfolder_); if (data->IsDirectory()) { archive_path.AppendFolder(data->Name()); } else { archive_path.SetFilename(data->Name()); } archive_path.SetFolderDelimiter('/'); archive_path.Normalize(); if (!CheckFilter(archive_path.pathname())) return SR_SUCCESS; if (archive_path.pathname().length() > 255) { // Cannot send a file name longer than 255 (yet) return SR_ERROR; } Pathname local_path(root_folder_); local_path.AppendPathname(archive_path.pathname()); local_path.Normalize(); if (data->IsDirectory()) { // Note: the NULL handle indicates that we need to open the folder next // time. find_.push_front(NULL); subfolder_ = archive_path.pathname(); } else { FileStream* stream = new FileStream; if (!stream->Open(local_path.pathname().c_str(), "rb")) { // TODO: Should this be an error? LOG_F(LS_WARNING) << "Couldn't open file: " << local_path.pathname(); delete stream; return SR_SUCCESS; } current_ = stream; current_bytes_ = data->FileSize(); } time_t modify_time = data->FileModifyTime(); std::string pathname = archive_path.pathname(); std::string magic, user, group, dev_major, dev_minor, prefix; std::string name = pathname; bool ustar = false; if (name.length() > 100) { ustar = true; // Put last 100 characters into the name, and rest in prefix size_t path_length = pathname.length(); prefix = pathname.substr(0, path_length - 100); name = pathname.substr(path_length - 100); } size_t block_data = 0; memset(block_, 0, BLOCK_SIZE); WriteFieldS(block_data, 100, name.c_str()); WriteFieldS(block_data, 8, data->IsDirectory() ? "777" : "666"); // mode WriteFieldS(block_data, 8, "5"); // owner uid WriteFieldS(block_data, 8, "5"); // owner gid WriteFieldN(block_data, 12, current_bytes_); WriteFieldN(block_data, 12, modify_time); WriteFieldS(block_data, 8, " "); // Checksum. To be filled in later. WriteFieldS(block_data, 1, data->IsDirectory() ? "5" : "0"); // link indicator (0 == normal file, 5 == directory) WriteFieldS(block_data, 100, ""); // name of linked file if (ustar) { WriteFieldS(block_data, 6, "ustar"); WriteFieldS(block_data, 2, ""); WriteFieldS(block_data, 32, user.c_str()); WriteFieldS(block_data, 32, group.c_str()); WriteFieldS(block_data, 8, dev_major.c_str()); WriteFieldS(block_data, 8, dev_minor.c_str()); WriteFieldS(block_data, 155, prefix.c_str()); } // Rest of the block must be empty StreamResult result = ProcessEmptyBlock(block_data, error); WriteChecksum(); block_pos_ = 0; if (current_bytes_ > 0) { next_block_ = data->IsDirectory() ? NB_FILE_HEADER : NB_DATA; } SignalNextEntry(archive_path.filename(), current_bytes_); return result;}void TarStream::WriteChecksum() { unsigned int sum = 0; for (int i = 0; i < BLOCK_SIZE; i++) sum += static_cast<unsigned char>(block_[i]); sprintf(block_ + 148, "%06o", sum); }bool TarStream::CheckFilter(const std::string& pathname) { if (filters_.empty()) return true; // pathname is allowed when there is a filter which: // A) Equals name // B) Matches a folder prefix of name for (size_t i=0; i<filters_.size(); ++i) { const std::string& filter = filters_[i]; // Make sure the filter is a prefix of name if (_strnicmp(pathname.c_str(), filter.data(), filter.length()) != 0) continue; // If the filter is not a directory, must match exactly if (!Pathname::IsFolderDelimiter(filter[filter.length()-1]) && (filter.length() != pathname.length())) continue; return true; } return false;}void TarStream::WriteFieldN(size_t& pos, size_t max_len, size_t numeric_field) { WriteFieldF(pos, max_len, "%.*o", max_len - 1, numeric_field);}void TarStream::WriteFieldS(size_t& pos, size_t max_len, const char* string_field) { ASSERT(pos + max_len <= BLOCK_SIZE); size_t len = strlen(string_field); size_t use_len = _min(len, max_len); memcpy(block_ + pos, string_field, use_len); pos += max_len;}void TarStream::WriteFieldF(size_t& pos, size_t max_len, const char* format, ...) { va_list args; va_start(args, format); char buffer[BLOCK_SIZE]; vsprintfn(buffer, ARRAY_SIZE(buffer), format, args); WriteFieldS(pos, max_len, buffer); va_end(args);}void TarStream::ReadFieldN(size_t& pos, size_t max_len, size_t* numeric_field) { ASSERT(NULL != numeric_field); std::string buffer; ReadFieldS(pos, max_len, &buffer); int value; if (!buffer.empty() && (1 == sscanf(buffer.c_str(), "%o", &value))) { *numeric_field = value; } else { *numeric_field = 0; }}void TarStream::ReadFieldS(size_t& pos, size_t max_len, std::string* string_field) { ASSERT(NULL != string_field); ASSERT(pos + max_len <= BLOCK_SIZE); size_t value_len = utils_base::strlenn(block_ + pos, max_len); string_field->assign(block_ + pos, value_len); ASSERT(talk_base::memory_check(block_ + pos + value_len, 0, max_len - value_len)); pos += max_len;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -