diff --git a/troposphere/haze/include/haze/ptp_object_heap.hpp b/troposphere/haze/include/haze/ptp_object_heap.hpp index fe1090a32..ac3484e5d 100644 --- a/troposphere/haze/include/haze/ptp_object_heap.hpp +++ b/troposphere/haze/include/haze/ptp_object_heap.hpp @@ -111,6 +111,14 @@ namespace haze { } constexpr void Deallocate(void *p, size_t n) { + /* Check for overflow in alignment. */ + if (!util::CanAddWithoutOverflow(n, alignof(u64) - 1)) { + return; + } + + /* Align the amount to satisfy allocation for u64. */ + n = util::AlignUp(n, alignof(u64)); + /* If the pointer was the last allocation, return the memory to the heap. */ if (static_cast(p) + n == this->GetNextAddress()) { m_next_address = this->GetNextAddress() - n; diff --git a/troposphere/haze/include/haze/ptp_responder.hpp b/troposphere/haze/include/haze/ptp_responder.hpp index 9bd1328e5..8dc996cdd 100644 --- a/troposphere/haze/include/haze/ptp_responder.hpp +++ b/troposphere/haze/include/haze/ptp_responder.hpp @@ -83,6 +83,7 @@ namespace haze { Result GetObjectPropDesc(PtpDataParser &dp); Result GetObjectPropValue(PtpDataParser &dp); Result SetObjectPropValue(PtpDataParser &dp); + Result GetObjectPropList(PtpDataParser &dp); }; } diff --git a/troposphere/haze/include/haze/ptp_responder_types.hpp b/troposphere/haze/include/haze/ptp_responder_types.hpp index d11416437..fd91e6d79 100644 --- a/troposphere/haze/include/haze/ptp_responder_types.hpp +++ b/troposphere/haze/include/haze/ptp_responder_types.hpp @@ -57,6 +57,7 @@ namespace haze { PtpOperationCode_MtpGetObjectPropDesc, PtpOperationCode_MtpGetObjectPropValue, PtpOperationCode_MtpSetObjectPropValue, + PtpOperationCode_MtpGetObjPropList, PtpOperationCode_AndroidGetPartialObject64, PtpOperationCode_AndroidSendPartialObject, PtpOperationCode_AndroidTruncateObject, diff --git a/troposphere/haze/include/haze/results.hpp b/troposphere/haze/include/haze/results.hpp index e8c501ffc..ea3933ddf 100644 --- a/troposphere/haze/include/haze/results.hpp +++ b/troposphere/haze/include/haze/results.hpp @@ -38,5 +38,7 @@ namespace haze { R_DEFINE_ERROR_RESULT(UnknownPropertyCode, 14); R_DEFINE_ERROR_RESULT(InvalidPropertyValue, 15); R_DEFINE_ERROR_RESULT(InvalidArgument, 16); + R_DEFINE_ERROR_RESULT(GroupSpecified, 17); + R_DEFINE_ERROR_RESULT(DepthSpecified, 18); } diff --git a/troposphere/haze/source/ptp_responder.cpp b/troposphere/haze/source/ptp_responder.cpp index ad8d4abb8..8194aaf7b 100644 --- a/troposphere/haze/source/ptp_responder.cpp +++ b/troposphere/haze/source/ptp_responder.cpp @@ -91,6 +91,12 @@ namespace haze { R_CATCH(haze::ResultInvalidPropertyValue) { R_TRY(this->WriteResponse(PtpResponseCode_MtpInvalidObjectPropValue)); } + R_CATCH(haze::ResultGroupSpecified) { + R_TRY(this->WriteResponse(PtpResponseCode_MtpSpecificationByGroupUnsupported)); + } + R_CATCH(haze::ResultDepthSpecified) { + R_TRY(this->WriteResponse(PtpResponseCode_MtpSpecificationByDepthUnsupported)); + } R_CATCH(haze::ResultInvalidArgument) { R_TRY(this->WriteResponse(PtpResponseCode_GeneralError)); } @@ -134,6 +140,7 @@ namespace haze { case PtpOperationCode_MtpGetObjectPropDesc: R_RETURN(this->GetObjectPropDesc(dp)); break; case PtpOperationCode_MtpGetObjectPropValue: R_RETURN(this->GetObjectPropValue(dp)); break; case PtpOperationCode_MtpSetObjectPropValue: R_RETURN(this->SetObjectPropValue(dp)); break; + case PtpOperationCode_MtpGetObjPropList: R_RETURN(this->GetObjectPropList(dp)); break; case PtpOperationCode_AndroidGetPartialObject64: R_RETURN(this->GetPartialObject64(dp)); break; case PtpOperationCode_AndroidSendPartialObject: R_RETURN(this->SendPartialObject(dp)); break; case PtpOperationCode_AndroidTruncateObject: R_RETURN(this->TruncateObject(dp)); break; diff --git a/troposphere/haze/source/ptp_responder_mtp_operations.cpp b/troposphere/haze/source/ptp_responder_mtp_operations.cpp index 973aa98a1..46fc824c2 100644 --- a/troposphere/haze/source/ptp_responder_mtp_operations.cpp +++ b/troposphere/haze/source/ptp_responder_mtp_operations.cpp @@ -113,9 +113,6 @@ 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()); @@ -198,6 +195,136 @@ namespace haze { R_RETURN(this->WriteResponse(PtpResponseCode_Ok)); } + Result PtpResponder::GetObjectPropList(PtpDataParser &dp) { + u32 object_id; + u32 object_format; + s32 property_code; + s32 group_code; + s32 depth; + + R_TRY(dp.Read(std::addressof(object_id))); + R_TRY(dp.Read(std::addressof(object_format))); + R_TRY(dp.Read(std::addressof(property_code))); + R_TRY(dp.Read(std::addressof(group_code))); + R_TRY(dp.Read(std::addressof(depth))); + R_TRY(dp.Finalize()); + + /* Ensure format is unspecified. */ + R_UNLESS(object_format == 0, haze::ResultInvalidArgument()); + + /* Ensure we have a valid property code. */ + R_UNLESS(property_code == -1 || IsSupportedObjectPropertyCode(PtpObjectPropertyCode(property_code)), haze::ResultUnknownPropertyCode()); + + /* Ensure group code is the default. */ + R_UNLESS(group_code == PtpPropertyGroupCode_Default, haze::ResultGroupSpecified()); + + /* Ensure depth is 0. */ + R_UNLESS(depth == 0, haze::ResultDepthSpecified()); + + /* 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()); + + /* Define helper for getting the object type. */ + const auto GetObjectType = [&] (FsDirEntryType *out_entry_type) { + R_RETURN(m_fs.GetEntryType(obj->GetName(), out_entry_type)); + }; + + /* Define helper for getting the object size. */ + const auto GetObjectSize = [&] (s64 *out_size) { + *out_size = 0; + + /* Check if this is a directory. */ + FsDirEntryType entry_type; + R_TRY(GetObjectType(std::addressof(entry_type))); + + /* If it is, we're done. */ + R_SUCCEED_IF(entry_type == FsDirEntryType_Dir); + + /* Otherwise, open as a file. */ + FsFile 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)); }; + + R_RETURN(m_fs.GetFileSize(std::addressof(file), out_size)); + }; + + /* Define helper for determining if the property should be included. */ + const auto ShouldIncludeProperty = [&] (PtpObjectPropertyCode code) { + /* If all properties were requested, or it was the requested property, we should include the property. */ + return property_code == -1 || code == property_code; + }; + + /* Begin writing the requested object properties. */ + PtpDataBuilder db(m_buffers->usb_bulk_write_buffer, std::addressof(m_usb_server)); + + R_TRY(db.WriteVariableLengthData(m_request_header, [&] { + for (const auto obj_property : SupportedObjectProperties) { + if (!ShouldIncludeProperty(obj_property)) { + continue; + } + + /* Write the object handle. */ + R_TRY(db.Add(object_id)); + + /* Write the property code. */ + R_TRY(db.Add(obj_property)); + + /* Write the property value. */ + switch (obj_property) { + case PtpObjectPropertyCode_PersistentUniqueObjectIdentifier: + { + R_TRY(db.Add(PtpDataTypeCode_U128)); + R_TRY(db.Add(object_id)); + } + break; + case PtpObjectPropertyCode_ObjectSize: + { + s64 size; + R_TRY(GetObjectSize(std::addressof(size))); + R_TRY(db.Add(PtpDataTypeCode_U64)); + R_TRY(db.Add(size)); + } + break; + case PtpObjectPropertyCode_StorageId: + { + R_TRY(db.Add(PtpDataTypeCode_U32)); + R_TRY(db.Add(StorageId_SdmcFs)); + } + break; + case PtpObjectPropertyCode_ParentObject: + { + R_TRY(db.Add(PtpDataTypeCode_U32)); + R_TRY(db.Add(obj->GetParentId())); + } + break; + case PtpObjectPropertyCode_ObjectFormat: + { + FsDirEntryType entry_type; + R_TRY(GetObjectType(std::addressof(entry_type))); + R_TRY(db.Add(PtpDataTypeCode_U16)); + R_TRY(db.Add(entry_type == FsDirEntryType_File ? PtpObjectFormatCode_Undefined : PtpObjectFormatCode_Association)); + } + break; + case PtpObjectPropertyCode_ObjectFileName: + { + R_TRY(db.Add(PtpDataTypeCode_String)); + R_TRY(db.AddString(std::strrchr(obj->GetName(), '/') + 1)); + } + break; + HAZE_UNREACHABLE_DEFAULT_CASE(); + } + } + + R_SUCCEED(); + })); + + /* Write the success response. */ + R_RETURN(this->WriteResponse(PtpResponseCode_Ok)); + } + Result PtpResponder::SetObjectPropValue(PtpDataParser &rdp) { u32 object_id; PtpObjectPropertyCode property_code;