blob: b556234ed44b423b6c6c2e9bd8bbfe4b2b9d6d46 [file] [log] [blame]
// Copyright 2024 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/storage/f2fs/xattr.h"
#include "src/storage/f2fs/file_cache.h"
namespace f2fs {
XattrOperator::XattrOperator(LockedPage &ipage, LockedPage &xattr_page)
: available_slots_(ipage ? kMaxXattrSlots : XattrSlots(kValidXattrBlockSize)) {
buffer_ = std::make_unique<std::array<xattr_slot_t, kMaxXattrSlots>>();
buffer_->fill(0);
if (ipage) {
Inode &inode = ipage->GetAddress<Node>()->i;
std::memcpy(buffer_->data(), &(inode.i_addr[kAddrsPerInode - kInlineXattrAddrs]),
safemath::checked_cast<size_t>(kXattrAlign * kInlineXattrAddrs));
}
if (xattr_page) {
ZX_DEBUG_ASSERT(xattr_page->Size() >= kValidXattrBlockSize);
std::memcpy(buffer_->data() + (ipage ? kInlineXattrAddrs : 0),
xattr_page->GetAddress<uint8_t>(), kValidXattrBlockSize);
}
if (buffer_->at(0) != kXattrMagic) {
XattrHeader header{
.magic = kXattrMagic,
.refcount = 1,
};
std::memcpy(buffer_->data(), &header, sizeof(XattrHeader));
}
}
zx::result<uint32_t> XattrOperator::FindSlotOffset(XattrIndex index, std::string_view name) {
uint32_t slot_offset = kXattrHeaderSlots;
while (slot_offset < available_slots_) {
XattrEntryInfo entry_info;
std::memcpy(&entry_info, &buffer_->at(slot_offset), sizeof(XattrEntryInfo));
if (entry_info.IsLast()) {
break;
}
if (static_cast<uint8_t>(index) == entry_info.name_index) {
ZX_ASSERT(slot_offset + entry_info.Slots() <= buffer_->size());
std::vector<char> entry(entry_info.Size());
std::memcpy(entry.data(), &buffer_->at(slot_offset), entry.size());
if (std::string(&entry.at(entry_info.NameOffset()), entry_info.name_len) == name) {
return zx::ok(slot_offset);
}
}
slot_offset += entry_info.Slots();
}
return zx::error(ZX_ERR_NOT_FOUND);
}
zx_status_t XattrOperator::Add(XattrIndex index, std::string_view name,
std::span<const uint8_t> value) {
uint32_t slot_offset = GetEndOffset();
if (slot_offset >= available_slots_) {
return ZX_ERR_NO_SPACE;
}
XattrEntryInfo new_info = {.name_index = static_cast<uint8_t>(index),
.name_len = safemath::checked_cast<uint8_t>(name.length()),
.value_size = safemath::checked_cast<uint16_t>(value.size())};
if (slot_offset + new_info.Slots() > available_slots_) {
return ZX_ERR_NO_SPACE;
}
std::vector<char> entry(new_info.Size());
std::memcpy(entry.data(), &new_info, sizeof(XattrEntryInfo));
std::memcpy(&entry.at(new_info.NameOffset()), name.data(), name.length());
std::memcpy(&entry.at(new_info.ValueOffset()), value.data(), value.size());
std::memcpy(&buffer_->at(slot_offset), entry.data(), entry.size());
return ZX_OK;
}
void XattrOperator::Remove(uint32_t offset) {
XattrEntryInfo entry_info;
std::memcpy(&entry_info, &buffer_->at(offset), sizeof(XattrEntryInfo));
uint32_t entry_slots = entry_info.Slots();
uint32_t next_entry_offset = offset + entry_slots;
if (next_entry_offset >= available_slots_) {
ZX_ASSERT(offset + entry_slots <= available_slots_);
std::fill(buffer_->begin() + offset, buffer_->begin() + offset + entry_slots, 0);
return;
}
uint32_t end_offset = GetEndOffset(next_entry_offset);
std::move(buffer_->begin() + next_entry_offset, buffer_->begin() + end_offset,
buffer_->begin() + offset);
std::fill(buffer_->begin() + end_offset - entry_slots, buffer_->begin() + end_offset, 0);
}
zx::result<size_t> XattrOperator::Lookup(XattrIndex index, std::string_view name,
std::span<uint8_t> out) {
zx::result<uint32_t> offset_or = FindSlotOffset(index, name);
if (offset_or.is_error()) {
return offset_or.take_error();
}
if (out.empty()) {
return zx::error(ZX_ERR_INVALID_ARGS);
}
XattrEntryInfo entry_info;
std::memcpy(&entry_info, &buffer_->at(*offset_or), sizeof(XattrEntryInfo));
ZX_ASSERT(*offset_or + entry_info.Slots() <= buffer_->size());
std::vector<char> entry(entry_info.Size());
std::memcpy(entry.data(), &buffer_->at(*offset_or), entry.size());
std::memcpy(out.data(), &entry.at(entry_info.ValueOffset()), entry_info.value_size);
return zx::ok(entry_info.value_size);
}
void XattrOperator::WriteTo(LockedPage &ipage, LockedPage &xattr_page) {
if (ipage) {
Inode &inode = ipage->GetAddress<Node>()->i;
std::memcpy(&(inode.i_addr[kAddrsPerInode - kInlineXattrAddrs]), buffer_->data(),
safemath::checked_cast<size_t>(kXattrAlign * kInlineXattrAddrs));
ipage.SetDirty();
}
if (xattr_page) {
ZX_DEBUG_ASSERT(xattr_page->Size() >= kValidXattrBlockSize);
std::memcpy(xattr_page->GetAddress<uint8_t>(),
buffer_->data() + (ipage ? kInlineXattrAddrs : 0), kValidXattrBlockSize);
xattr_page.SetDirty();
}
}
uint32_t XattrOperator::GetEndOffset(uint32_t from) {
from = std::max(from, safemath::checked_cast<uint32_t>(kXattrHeaderSlots));
while (from < available_slots_) {
XattrEntryInfo entry_info;
std::memcpy(&entry_info, &buffer_->at(from), sizeof(XattrEntryInfo));
if (entry_info.IsLast()) {
break;
}
from += entry_info.Slots();
}
return from;
}
} // namespace f2fs