haze: add SetObjectPropValue

This commit is contained in:
Liam 2023-04-16 13:50:13 -04:00
parent 409399d690
commit 20c6b174c6
5 changed files with 287 additions and 135 deletions

View file

@ -71,6 +71,10 @@ namespace haze {
R_RETURN(this->ForwardResult(fsFsDeleteFile, m_filesystem, path));
}
Result RenameFile(const char *old_path, const char *new_path) {
R_RETURN(this->ForwardResult(fsFsRenameFile, m_filesystem, old_path, new_path));
}
Result OpenFile(const char *path, u32 mode, FsFile *out_file) {
R_RETURN(this->ForwardResult(fsFsOpenFile, m_filesystem, path, mode, out_file));
}
@ -103,6 +107,10 @@ namespace haze {
R_RETURN(this->ForwardResult(fsFsDeleteDirectoryRecursively, m_filesystem, path));
}
Result RenameDirectory(const char *old_path, const char *new_path) {
R_RETURN(this->ForwardResult(fsFsRenameDirectory, m_filesystem, old_path, new_path));
}
Result OpenDirectory(const char *path, u32 mode, FsDir *out_dir) {
R_RETURN(this->ForwardResult(fsFsOpenDirectory, m_filesystem, path, mode, out_dir));
}

View file

@ -21,62 +21,63 @@
namespace haze {
class PtpObjectDatabase {
struct PtpObject {
public:
struct ObjectNode {
public:
util::IntrusiveRedBlackTreeNode m_object_name_to_id_node;
util::IntrusiveRedBlackTreeNode m_object_id_to_name_node;
u32 m_parent_id;
u32 m_object_id;
char m_name[];
public:
explicit ObjectNode(char *name, u32 parent_id, u32 object_id) : m_object_name_to_id_node(), m_object_id_to_name_node(), m_parent_id(parent_id), m_object_id(object_id) { /* ... */ }
util::IntrusiveRedBlackTreeNode m_object_name_to_id_node;
util::IntrusiveRedBlackTreeNode m_object_id_to_name_node;
u32 m_parent_id;
u32 m_object_id;
char m_name[];
public:
explicit PtpObject(char *name, u32 parent_id, u32 object_id) : m_object_name_to_id_node(), m_object_id_to_name_node(), m_parent_id(parent_id), m_object_id(object_id) { /* ... */ }
const char *GetName() const { return m_name; }
u32 GetParentId() const { return m_parent_id; }
u32 GetObjectId() const { return m_object_id; }
const char *GetName() const { return m_name; }
u32 GetParentId() const { return m_parent_id; }
u32 GetObjectId() const { return m_object_id; }
bool GetIsRegistered() const { return m_object_id != 0; }
struct NameComparator {
struct RedBlackKeyType {
const char *m_name;
struct NameComparator {
struct RedBlackKeyType {
const char *m_name;
constexpr RedBlackKeyType(const char *name) : m_name(name) { /* ... */ }
constexpr RedBlackKeyType(const char *name) : m_name(name) { /* ... */ }
constexpr const char *GetName() const {
return m_name;
}
};
constexpr const char *GetName() const {
return m_name;
}
};
template<typename T> requires (std::same_as<T, ObjectNode> || std::same_as<T, RedBlackKeyType>)
static constexpr int Compare(const T &lhs, const ObjectNode &rhs) {
return std::strcmp(lhs.GetName(), rhs.GetName());
}
};
struct IdComparator {
struct RedBlackKeyType {
u32 m_object_id;
constexpr RedBlackKeyType(u32 object_id) : m_object_id(object_id) { /* ... */ }
constexpr u32 GetObjectId() const {
return m_object_id;
}
};
template<typename T> requires (std::same_as<T, ObjectNode> || std::same_as<T, RedBlackKeyType>)
static constexpr int Compare(const T &lhs, const ObjectNode &rhs) {
return lhs.GetObjectId() - rhs.GetObjectId();
}
};
template<typename T> requires (std::same_as<T, PtpObject> || std::same_as<T, RedBlackKeyType>)
static constexpr int Compare(const T &lhs, const PtpObject &rhs) {
return std::strcmp(lhs.GetName(), rhs.GetName());
}
};
private:
using ObjectNameToIdTreeTraits = util::IntrusiveRedBlackTreeMemberTraitsDeferredAssert<&ObjectNode::m_object_name_to_id_node>;
using ObjectNameToIdTree = ObjectNameToIdTreeTraits::TreeType<ObjectNode::NameComparator>;
using ObjectIdToNameTreeTraits = util::IntrusiveRedBlackTreeMemberTraitsDeferredAssert<&ObjectNode::m_object_id_to_name_node>;
using ObjectIdToNameTree = ObjectIdToNameTreeTraits::TreeType<ObjectNode::IdComparator>;
struct IdComparator {
struct RedBlackKeyType {
u32 m_object_id;
constexpr RedBlackKeyType(u32 object_id) : m_object_id(object_id) { /* ... */ }
constexpr u32 GetObjectId() const {
return m_object_id;
}
};
template<typename T> requires (std::same_as<T, PtpObject> || std::same_as<T, RedBlackKeyType>)
static constexpr int Compare(const T &lhs, const PtpObject &rhs) {
return lhs.GetObjectId() - rhs.GetObjectId();
}
};
};
class PtpObjectDatabase {
private:
using ObjectNameToIdTreeTraits = util::IntrusiveRedBlackTreeMemberTraitsDeferredAssert<&PtpObject::m_object_name_to_id_node>;
using ObjectNameToIdTree = ObjectNameToIdTreeTraits::TreeType<PtpObject::NameComparator>;
using ObjectIdToNameTreeTraits = util::IntrusiveRedBlackTreeMemberTraitsDeferredAssert<&PtpObject::m_object_id_to_name_node>;
using ObjectIdToNameTree = ObjectIdToNameTreeTraits::TreeType<PtpObject::IdComparator>;
PtpObjectHeap *m_object_heap;
ObjectNameToIdTree m_name_to_object_id;
@ -89,9 +90,15 @@ namespace haze {
void Finalize();
public:
/* Object database API. */
Result AddObjectId(const char *parent_name, const char *name, u32 *out_object_id, u32 parent_id, u32 desired_object_id = 0);
void RemoveObjectId(u32 object_id);
ObjectNode *GetObject(u32 object_id);
Result CreateOrFindObject(const char *parent_name, const char *name, u32 parent_id, PtpObject **out_object);
void RegisterObject(PtpObject *object, u32 desired_id = 0);
void UnregisterObject(PtpObject *object);
void DeleteObject(PtpObject *obj);
Result CreateAndRegisterObjectId(const char *parent_name, const char *name, u32 parent_id, u32 *out_object_id);
public:
PtpObject *GetObjectById(u32 object_id);
PtpObject *GetObjectByName(const char *name);
};
}

View file

@ -30,11 +30,12 @@ namespace haze {
R_DEFINE_ERROR_RESULT(UnknownPacketType, 6);
R_DEFINE_ERROR_RESULT(SessionNotOpen, 7);
R_DEFINE_ERROR_RESULT(OutOfMemory, 8);
R_DEFINE_ERROR_RESULT(ObjectNotFound, 9);
R_DEFINE_ERROR_RESULT(StorageNotFound, 10);
R_DEFINE_ERROR_RESULT(InvalidObjectId, 9);
R_DEFINE_ERROR_RESULT(InvalidStorageId, 10);
R_DEFINE_ERROR_RESULT(OperationNotSupported, 11);
R_DEFINE_ERROR_RESULT(UnknownRequestType, 12);
R_DEFINE_ERROR_RESULT(UnknownPropertyCode, 13);
R_DEFINE_ERROR_RESULT(GeneralFailure, 14);
R_DEFINE_ERROR_RESULT(InvalidPropertyValue, 14);
R_DEFINE_ERROR_RESULT(GeneralFailure, 15);
}

View file

@ -37,31 +37,32 @@ namespace haze {
m_object_heap = nullptr;
}
Result PtpObjectDatabase::AddObjectId(const char *parent_name, const char *name, u32 *out_object_id, u32 parent_id, u32 desired_object_id) {
/* Calculate length of the name. */
const size_t parent_name_len = std::strlen(parent_name);
const size_t name_len = std::strlen(name) + 1;
const size_t alloc_len = parent_name_len + 1 + name_len;
Result PtpObjectDatabase::CreateOrFindObject(const char *parent_name, const char *name, u32 parent_id, PtpObject **out_object) {
constexpr auto separator = "/";
/* Calculate length of the new name with null terminator. */
const size_t parent_name_len = util::Strlen(parent_name);
const size_t separator_len = util::Strlen(separator);
const size_t name_len = util::Strlen(name);
const size_t terminator_len = 1;
const size_t alloc_len = sizeof(PtpObject) + parent_name_len + separator_len + name_len + terminator_len;
/* Allocate memory for the node. */
ObjectNode * const node = m_object_heap->Allocate<ObjectNode>(sizeof(ObjectNode) + alloc_len);
R_UNLESS(node != nullptr, haze::ResultOutOfMemory());
PtpObject * const object = m_object_heap->Allocate<PtpObject>(alloc_len);
R_UNLESS(object != nullptr, haze::ResultOutOfMemory());
{
/* Ensure we maintain a clean state on failure. */
auto guard = SCOPE_GUARD { m_object_heap->Deallocate(node, sizeof(ObjectNode) + alloc_len); };
auto guard = SCOPE_GUARD { m_object_heap->Deallocate(object, alloc_len); };
/* Take ownership of the name. */
std::strncpy(node->m_name, parent_name, parent_name_len + 1);
node->m_name[parent_name_len] = '/';
std::strncpy(node->m_name + parent_name_len + 1, name, name_len);
std::strncpy(object->m_name, parent_name, parent_name_len + terminator_len);
std::strncpy(object->m_name + parent_name_len, separator, separator_len + terminator_len);
std::strncpy(object->m_name + parent_name_len + separator_len, name, name_len + terminator_len);
/* Check if an object with this name already exists. If it does, we can just return it here. */
auto it = m_name_to_object_id.find_key(node->m_name);
if (it != m_name_to_object_id.end()) {
if (out_object_id) {
*out_object_id = it->GetObjectId();
}
if (auto * const existing = this->GetObjectByName(object->GetName()); existing != nullptr) {
*out_object = existing;
R_SUCCEED();
}
@ -69,37 +70,70 @@ namespace haze {
guard.Cancel();
}
/* Insert node into trees. */
node->m_parent_id = parent_id;
node->m_object_id = desired_object_id == 0 ? m_next_object_id++ : desired_object_id;
m_name_to_object_id.insert(*node);
m_object_id_to_name.insert(*node);
/* Set node properties. */
object->m_parent_id = parent_id;
object->m_object_id = 0;
/* Set output. */
if (out_object_id) {
*out_object_id = node->GetObjectId();
}
*out_object = object;
/* We succeeded. */
R_SUCCEED();
}
void PtpObjectDatabase::RemoveObjectId(u32 object_id) {
/* Find in forward mapping. */
auto it = m_object_id_to_name.find_key(object_id);
if (it == m_object_id_to_name.end()) {
void PtpObjectDatabase::RegisterObject(PtpObject *object, u32 desired_id) {
/* If the object is already registered, skip registration. */
if (object->GetIsRegistered()) {
return;
}
/* Free the node. */
ObjectNode *node = std::addressof(*it);
m_object_id_to_name.erase(m_object_id_to_name.iterator_to(*node));
m_name_to_object_id.erase(m_name_to_object_id.iterator_to(*node));
m_object_heap->Deallocate(node, sizeof(ObjectNode) + std::strlen(node->GetName()) + 1);
/* Set desired object ID. */
if (desired_id == 0) {
desired_id = m_next_object_id++;
}
/* Insert node into trees. */
object->m_object_id = desired_id;
m_name_to_object_id.insert(*object);
m_object_id_to_name.insert(*object);
}
PtpObjectDatabase::ObjectNode *PtpObjectDatabase::GetObject(u32 object_id) {
/* Find in forward mapping. */
void PtpObjectDatabase::UnregisterObject(PtpObject *object) {
/* If the object is not registered, skip trying to unregister. */
if (!object->GetIsRegistered()) {
return;
}
/* Remove node from trees. */
m_object_id_to_name.erase(m_object_id_to_name.iterator_to(*object));
m_name_to_object_id.erase(m_name_to_object_id.iterator_to(*object));
object->m_object_id = 0;
}
void PtpObjectDatabase::DeleteObject(PtpObject *object) {
/* Unregister the object as required. */
this->UnregisterObject(object);
/* Free the object. */
m_object_heap->Deallocate(object, sizeof(PtpObject) + std::strlen(object->GetName()) + 1);
}
Result PtpObjectDatabase::CreateAndRegisterObjectId(const char *parent_name, const char *name, u32 parent_id, u32 *out_object_id) {
/* Try to create the object. */
PtpObject *object;
R_TRY(this->CreateOrFindObject(parent_name, name, parent_id, std::addressof(object)));
/* We succeeded, so register it. */
this->RegisterObject(object);
/* Set the output ID. */
*out_object_id = object->GetObjectId();
R_SUCCEED();
}
PtpObject *PtpObjectDatabase::GetObjectById(u32 object_id) {
/* Find in ID mapping. */
auto it = m_object_id_to_name.find_key(object_id);
if (it == m_object_id_to_name.end()) {
return nullptr;
@ -108,4 +142,13 @@ namespace haze {
return std::addressof(*it);
}
PtpObject *PtpObjectDatabase::GetObjectByName(const char *name) {
/* Find in name mapping. */
auto it = m_name_to_object_id.find_key(name);
if (it == m_name_to_object_id.end()) {
return nullptr;
}
return std::addressof(*it);
}
}

View file

@ -205,15 +205,18 @@ namespace haze {
R_CATCH(haze::ResultOperationNotSupported) {
R_TRY(this->WriteResponse(PtpResponseCode_OperationNotSupported));
}
R_CATCH(haze::ResultStorageNotFound) {
R_CATCH(haze::ResultInvalidStorageId) {
R_TRY(this->WriteResponse(PtpResponseCode_InvalidStorageId));
}
R_CATCH(haze::ResultObjectNotFound) {
R_CATCH(haze::ResultInvalidObjectId) {
R_TRY(this->WriteResponse(PtpResponseCode_InvalidObjectHandle));
}
R_CATCH(haze::ResultUnknownPropertyCode) {
R_TRY(this->WriteResponse(PtpResponseCode_MtpObjectPropNotSupported));
}
R_CATCH(haze::ResultInvalidPropertyValue) {
R_TRY(this->WriteResponse(PtpResponseCode_MtpInvalidObjectPropValue));
}
R_CATCH_MODULE(fs) {
/* Errors from fs are typically recoverable. */
R_TRY(this->WriteResponse(PtpResponseCode_GeneralError));
@ -313,11 +316,18 @@ namespace haze {
/* Close, if we're already open. */
this->ForceCloseSession();
/* Initialize the database with hardcoded storage IDs. */
/* Initialize the database. */
m_session_open = true;
m_object_database.Initialize(m_object_heap);
R_TRY(m_object_database.AddObjectId("", "", nullptr, PtpGetObjectHandles_RootParent, StorageId_SdmcFs));
/* Create the root storages. */
PtpObject *object;
R_TRY(m_object_database.CreateOrFindObject("", "", PtpGetObjectHandles_RootParent, std::addressof(object)));
/* Register the root storages. */
m_object_database.RegisterObject(object, StorageId_SdmcFs);
/* We succeeded. */
R_RETURN(this->WriteResponse(PtpResponseCode_Ok));
}
@ -365,7 +375,7 @@ namespace haze {
}
break;
default:
R_THROW(haze::ResultStorageNotFound());
R_THROW(haze::ResultInvalidStorageId());
}
/* Write the result. */
@ -406,12 +416,12 @@ namespace haze {
}
/* Check if we know about the object. If we don't, it's an error. */
auto * const fileobj = m_object_database.GetObject(association_object_handle);
R_UNLESS(fileobj != nullptr, haze::ResultObjectNotFound());
auto * const obj = m_object_database.GetObjectById(association_object_handle);
R_UNLESS(obj != nullptr, haze::ResultInvalidObjectId());
/* Try to read the object as a directory. */
FsDir dir;
R_TRY(m_fs.OpenDirectory(fileobj->GetName(), FsDirOpenMode_ReadDirs | FsDirOpenMode_ReadFiles, std::addressof(dir)));
R_TRY(m_fs.OpenDirectory(obj->GetName(), FsDirOpenMode_ReadDirs | FsDirOpenMode_ReadFiles, std::addressof(dir)));
/* Ensure we maintain a clean state on exit. */
ON_SCOPE_EXIT { m_fs.CloseDirectory(std::addressof(dir)); };
@ -435,7 +445,7 @@ namespace haze {
/* Write to output. */
for (s64 i = 0; i < read_count; i++) {
u32 handle;
R_TRY(m_object_database.AddObjectId(fileobj->GetName(), g_dir_entries[i].name, std::addressof(handle), fileobj->GetObjectId()));
R_TRY(m_object_database.CreateAndRegisterObjectId(obj->GetName(), g_dir_entries[i].name, obj->GetObjectId(), std::addressof(handle)));
R_TRY(db.Add(handle));
}
@ -459,8 +469,8 @@ namespace haze {
R_TRY(dp.Finalize());
/* Check if we know about the object. If we don't, it's an error. */
auto * const fileobj = m_object_database.GetObject(object_id);
R_UNLESS(fileobj != nullptr, haze::ResultObjectNotFound());
auto * const obj = m_object_database.GetObjectById(object_id);
R_UNLESS(obj != nullptr, haze::ResultInvalidObjectId());
/* Build info about the object. */
PtpObjectInfo object_info(DefaultObjectInfo);
@ -473,13 +483,13 @@ namespace haze {
} else {
/* Figure out what type of object this is. */
FsDirEntryType entry_type;
R_TRY(m_fs.GetEntryType(fileobj->GetName(), std::addressof(entry_type)));
R_TRY(m_fs.GetEntryType(obj->GetName(), std::addressof(entry_type)));
/* Get the size, if we are requesting info about a file. */
s64 size = 0;
if (entry_type == FsDirEntryType_File) {
FsFile file;
R_TRY(m_fs.OpenFile(fileobj->GetName(), FsOpenMode_Read, std::addressof(file)));
R_TRY(m_fs.OpenFile(obj->GetName(), FsOpenMode_Read, std::addressof(file)));
/* Ensure we maintain a clean state on exit. */
ON_SCOPE_EXIT { m_fs.CloseFile(std::addressof(file)); };
@ -487,9 +497,9 @@ namespace haze {
R_TRY(m_fs.GetFileSize(std::addressof(file), std::addressof(size)));
}
object_info.filename = std::strrchr(fileobj->GetName(), '/') + 1;
object_info.filename = std::strrchr(obj->GetName(), '/') + 1;
object_info.object_compressed_size = size;
object_info.parent_object = fileobj->GetParentId();
object_info.parent_object = obj->GetParentId();
if (entry_type == FsDirEntryType_Dir) {
object_info.object_format = PtpObjectFormatCode_Association;
@ -536,12 +546,12 @@ namespace haze {
R_TRY(dp.Finalize());
/* Check if we know about the object. If we don't, it's an error. */
auto * const fileobj = m_object_database.GetObject(object_id);
R_UNLESS(fileobj != nullptr, haze::ResultObjectNotFound());
auto * const obj = m_object_database.GetObjectById(object_id);
R_UNLESS(obj != nullptr, haze::ResultInvalidObjectId());
/* Lock the object as a file. */
FsFile file;
R_TRY(m_fs.OpenFile(fileobj->GetName(), FsOpenMode_Read, std::addressof(file)));
R_TRY(m_fs.OpenFile(obj->GetName(), FsOpenMode_Read, std::addressof(file)));
/* Ensure we maintain a clean state on exit. */
ON_SCOPE_EXIT { m_fs.CloseFile(std::addressof(file)); };
@ -585,7 +595,7 @@ namespace haze {
PtpDataParser dp(g_bulk_read_buffer, std::addressof(m_usb_server));
PtpObjectInfo info(DefaultObjectInfo);
/* Ensure that we have a data header. */
/* Ensure we have a data header. */
PtpUsbBulkContainer data_header;
R_TRY(dp.Read(std::addressof(data_header)));
R_UNLESS(data_header.type == PtpUsbBulkContainerType_Data, haze::ResultUnknownRequestType());
@ -620,29 +630,31 @@ namespace haze {
}
/* Check if we know about the parent object. If we don't, it's an error. */
auto * const parentobj = m_object_database.GetObject(parent_object);
R_UNLESS(parentobj != nullptr, haze::ResultObjectNotFound());
auto * const parentobj = m_object_database.GetObjectById(parent_object);
R_UNLESS(parentobj != nullptr, haze::ResultInvalidObjectId());
/* Make a new object with the intended name. */
PtpNewObjectInfo new_object_info;
new_object_info.storage_id = StorageId_SdmcFs;
new_object_info.parent_object_id = parent_object == storage_id ? 0 : parent_object;
R_TRY(m_object_database.AddObjectId(parentobj->GetName(), g_filename_str, std::addressof(new_object_info.object_id), parentobj->GetObjectId()));
/* Create the object in the database. */
PtpObject *obj;
R_TRY(m_object_database.CreateOrFindObject(parentobj->GetName(), g_filename_str, parentobj->GetObjectId(), std::addressof(obj)));
/* Ensure we maintain a clean state on failure. */
ON_RESULT_FAILURE { m_object_database.RemoveObjectId(new_object_info.object_id); };
ON_RESULT_FAILURE { m_object_database.DeleteObject(obj); };
/* Get the object name we just built. */
auto * const fileobj = m_object_database.GetObject(new_object_info.object_id);
R_UNLESS(fileobj != nullptr, haze::ResultGeneralFailure());
/* Register the object with a new ID. */
m_object_database.RegisterObject(obj);
new_object_info.object_id = obj->GetObjectId();
/* Create the object on the filesystem. */
if (info.object_format == PtpObjectFormatCode_Association) {
R_TRY(m_fs.CreateDirectory(fileobj->GetName()));
R_TRY(m_fs.CreateDirectory(obj->GetName()));
m_send_object_id = 0;
} else {
R_TRY(m_fs.CreateFile(fileobj->GetName(), 0, 0));
R_TRY(m_fs.CreateFile(obj->GetName(), 0, 0));
m_send_object_id = new_object_info.object_id;
}
@ -658,7 +670,7 @@ namespace haze {
PtpDataParser dp(g_bulk_read_buffer, std::addressof(m_usb_server));
/* Ensure that we have a data header. */
/* Ensure we have a data header. */
PtpUsbBulkContainer data_header;
R_TRY(dp.Read(std::addressof(data_header)));
R_UNLESS(data_header.type == PtpUsbBulkContainerType_Data, haze::ResultUnknownRequestType());
@ -666,12 +678,12 @@ namespace haze {
R_UNLESS(data_header.trans_id == m_request_header.trans_id, haze::ResultOperationNotSupported());
/* Check if we know about the object. If we don't, it's an error. */
auto * const fileobj = m_object_database.GetObject(m_send_object_id);
R_UNLESS(fileobj != nullptr, haze::ResultObjectNotFound());
auto * const obj = m_object_database.GetObjectById(m_send_object_id);
R_UNLESS(obj != nullptr, haze::ResultInvalidObjectId());
/* Lock the object as a file. */
FsFile file;
R_TRY(m_fs.OpenFile(fileobj->GetName(), FsOpenMode_Write | FsOpenMode_Append, std::addressof(file)));
R_TRY(m_fs.OpenFile(obj->GetName(), FsOpenMode_Write | FsOpenMode_Append, std::addressof(file)));
/* Ensure we maintain a clean state on exit. */
ON_SCOPE_EXIT { m_fs.CloseFile(std::addressof(file)); };
@ -708,23 +720,26 @@ namespace haze {
R_TRY(dp.Read(std::addressof(object_id)));
R_TRY(dp.Finalize());
/* Disallow deleting the storage root. */
R_UNLESS(object_id != StorageId_SdmcFs, haze::ResultInvalidObjectId());
/* Check if we know about the object. If we don't, it's an error. */
auto * const fileobj = m_object_database.GetObject(object_id);
R_UNLESS(fileobj != nullptr, haze::ResultObjectNotFound());
auto * const obj = m_object_database.GetObjectById(object_id);
R_UNLESS(obj != nullptr, haze::ResultInvalidObjectId());
/* Figure out what type of object this is. */
FsDirEntryType entry_type;
R_TRY(m_fs.GetEntryType(fileobj->GetName(), std::addressof(entry_type)));
R_TRY(m_fs.GetEntryType(obj->GetName(), std::addressof(entry_type)));
/* Remove the object from the filesystem. */
if (entry_type == FsDirEntryType_Dir) {
R_TRY(m_fs.DeleteDirectoryRecursively(fileobj->GetName()));
R_TRY(m_fs.DeleteDirectoryRecursively(obj->GetName()));
} else {
R_TRY(m_fs.DeleteFile(fileobj->GetName()));
R_TRY(m_fs.DeleteFile(obj->GetName()));
}
/* Remove the object from tracking. */
m_object_database.RemoveObjectId(fileobj->GetObjectId());
/* Remove the object from the database. */
m_object_database.DeleteObject(obj);
/* We succeeded. */
R_RETURN(this->WriteResponse(PtpResponseCode_Ok));
@ -816,22 +831,25 @@ namespace haze {
R_TRY(dp.Read(std::addressof(property_code)));
R_TRY(dp.Finalize());
/* Disallow renaming the storage root. */
R_UNLESS(object_id != StorageId_SdmcFs, haze::ResultInvalidObjectId());
/* Ensure we have a valid property code before continuing. */
R_UNLESS(IsSupportedObjectPropertyCode(property_code), haze::ResultUnknownPropertyCode());
/* Check if we know about the object. If we don't, it's an error. */
auto * const fileobj = m_object_database.GetObject(object_id);
R_UNLESS(fileobj != nullptr, haze::ResultObjectNotFound());
auto * const obj = m_object_database.GetObjectById(object_id);
R_UNLESS(obj != nullptr, haze::ResultInvalidObjectId());
/* Get the object type. */
FsDirEntryType entry_type;
R_TRY(m_fs.GetEntryType(fileobj->GetName(), std::addressof(entry_type)));
R_TRY(m_fs.GetEntryType(obj->GetName(), std::addressof(entry_type)));
/* Get the object size. */
s64 size = 0;
if (entry_type == FsDirEntryType_File) {
FsFile file;
R_TRY(m_fs.OpenFile(fileobj->GetName(), FsOpenMode_Read, std::addressof(file)));
R_TRY(m_fs.OpenFile(obj->GetName(), FsOpenMode_Read, std::addressof(file)));
/* Ensure we maintain a clean state on exit. */
ON_SCOPE_EXIT { m_fs.CloseFile(std::addressof(file)); };
@ -854,7 +872,7 @@ namespace haze {
R_TRY(db.Add(entry_type == FsDirEntryType_File ? PtpObjectFormatCode_Undefined : PtpObjectFormatCode_Association));
break;
case PtpObjectPropertyCode_ObjectFileName:
R_TRY(db.AddString(std::strrchr(fileobj->GetName(), '/') + 1));
R_TRY(db.AddString(std::strrchr(obj->GetName(), '/') + 1));
break;
HAZE_UNREACHABLE_DEFAULT_CASE();
}
@ -866,7 +884,82 @@ namespace haze {
R_RETURN(this->WriteResponse(PtpResponseCode_Ok));
}
Result PtpResponder::SetObjectPropValue(PtpDataParser &dp) {
R_THROW(haze::ResultOperationNotSupported());
Result PtpResponder::SetObjectPropValue(PtpDataParser &rdp) {
u32 object_id;
PtpObjectPropertyCode property_code;
R_TRY(rdp.Read(std::addressof(object_id)));
R_TRY(rdp.Read(std::addressof(property_code)));
R_TRY(rdp.Finalize());
PtpDataParser dp(g_bulk_read_buffer, std::addressof(m_usb_server));
/* Ensure we have a data header. */
PtpUsbBulkContainer data_header;
R_TRY(dp.Read(std::addressof(data_header)));
R_UNLESS(data_header.type == PtpUsbBulkContainerType_Data, haze::ResultUnknownRequestType());
R_UNLESS(data_header.code == m_request_header.code, haze::ResultOperationNotSupported());
R_UNLESS(data_header.trans_id == m_request_header.trans_id, haze::ResultOperationNotSupported());
/* Ensure we have a valid property code before continuing. */
R_UNLESS(property_code == PtpObjectPropertyCode_ObjectFileName, haze::ResultUnknownPropertyCode());
/* Check if we know about the object. If we don't, it's an error. */
auto * const obj = m_object_database.GetObjectById(object_id);
R_UNLESS(obj != nullptr, haze::ResultInvalidObjectId());
/* We are reading a file name. */
R_TRY(dp.ReadString(g_filename_str));
R_TRY(dp.Finalize());
/* Ensure we can actually process the new name. */
const bool is_empty = g_filename_str[0] == '\x00';
const bool contains_slashes = std::strchr(g_filename_str, '/') != nullptr;
R_UNLESS(!is_empty && !contains_slashes, haze::ResultInvalidPropertyValue());
/* Add a new object in the database with the new name. */
PtpObject *newobj;
{
/* Find the last path separator in the existing object name. */
char *pathsep = std::strrchr(obj->m_name, '/');
HAZE_ASSERT(pathsep != nullptr);
/* Temporarily mark the path separator as null to facilitate processing. */
*pathsep = '\x00';
ON_SCOPE_EXIT { *pathsep = '/'; };
R_TRY(m_object_database.CreateOrFindObject(obj->GetName(), g_filename_str, obj->GetParentId(), std::addressof(newobj)));
}
{
/* Ensure we maintain a clean state on failure. */
ON_RESULT_FAILURE {
if (!newobj->GetIsRegistered()) {
/* Only delete if the object was not registered. */
/* Otherwise, we would remove an object that still exists. */
m_object_database.DeleteObject(newobj);
}
};
/* Get the old object type. */
FsDirEntryType entry_type;
R_TRY(m_fs.GetEntryType(obj->GetName(), std::addressof(entry_type)));
/* Attempt to rename the object on the filesystem. */
if (entry_type == FsDirEntryType_Dir) {
R_TRY(m_fs.RenameDirectory(obj->GetName(), newobj->GetName()));
} else {
R_TRY(m_fs.RenameFile(obj->GetName(), newobj->GetName()));
}
}
/* Unregister and free the old object. */
m_object_database.DeleteObject(obj);
/* Register the new object. */
m_object_database.RegisterObject(newobj, object_id);
/* We succeeded. */
R_RETURN(this->WriteResponse(PtpResponseCode_Ok));
}
}