unix: handle MSG_PEEK on receive.

fix #18653

Change-Id: Ideaba943644481ffd6cf3e1801069508b7362c9a
Reviewed-on: https://review.haiku-os.org/c/haiku/+/9860
Reviewed-by: Jérôme Duval <[email protected]>
This commit is contained in:
Jérôme Duval
2025-11-15 22:30:45 +00:00
parent 431d197b7e
commit 139bf65f0d
5 changed files with 81 additions and 22 deletions
@@ -327,7 +327,7 @@ UnixDatagramEndpoint::Receive(const iovec* vecs, size_t vecCount,
TRACE("[%" B_PRId32 "] %p->UnixDatagramEndpoint::Receive()\n",
find_thread(NULL), this);
if ((flags & ~(MSG_DONTWAIT)) != 0)
if ((flags & ~(MSG_DONTWAIT | MSG_PEEK)) != 0)
return EOPNOTSUPP;
bigtime_t timeout = 0;
@@ -370,7 +370,8 @@ UnixDatagramEndpoint::Receive(const iovec* vecs, size_t vecCount,
struct sockaddr_storage addressStorage;
ssize_t result = fifo->Read(vecs, vecCount, _ancillaryData, &addressStorage, timeout);
ssize_t result = fifo->Read(vecs, vecCount, _ancillaryData, &addressStorage, timeout,
(flags & MSG_PEEK) != 0);
// Notify select()ing writers, if we successfully read anything.
size_t writable = fifo->Writable();
@@ -25,7 +25,7 @@
UnixRequest::UnixRequest(const iovec* vecs, size_t count,
ancillary_data_container* ancillaryData,
struct sockaddr_storage* address)
struct sockaddr_storage* address, bool clone)
:
fVecs(vecs),
fVecCount(count),
@@ -34,7 +34,8 @@ UnixRequest::UnixRequest(const iovec* vecs, size_t count,
fBytesTransferred(0),
fVecIndex(0),
fVecOffset(0),
fAddress(address)
fAddress(address),
fClone(clone)
{
for (size_t i = 0; i < fVecCount; i++)
fTotalSize += fVecs[i].iov_len;
@@ -94,6 +95,19 @@ UnixRequest::AddAncillaryData(ancillary_data_container* data)
}
status_t
UnixRequest::CloneAncillaryData(ancillary_data_container* data)
{
if (fAncillaryData == NULL) {
fAncillaryData = gStackModule->create_ancillary_data_container();
if (fAncillaryData == NULL)
return B_NO_MEMORY;
}
return gStackModule->clone_ancillary_data(data, fAncillaryData);
}
// #pragma mark - UnixBufferQueue
@@ -147,6 +161,7 @@ UnixBufferQueue::Read(UnixRequest& request)
bool user = gStackModule->is_syscall();
size_t readable = Readable();
const bool clone = request.IsClone();
void* data;
size_t size;
@@ -168,19 +183,31 @@ UnixBufferQueue::Read(UnixRequest& request)
size = readable;
ssize_t bytesRead;
if (user)
if (user) {
if (clone) {
bytesRead = ring_buffer_user_peek(fBuffer, request.BytesTransferred(),
(uint8*)data, size);
} else {
bytesRead = ring_buffer_user_read(fBuffer, (uint8*)data, size);
else
}
} else {
if (clone) {
bytesRead = ring_buffer_peek(fBuffer, request.BytesTransferred(),
(uint8*)data, size);
} else {
bytesRead = ring_buffer_read(fBuffer, (uint8*)data, size);
}
}
if (bytesRead < 0)
return bytesRead;
if (bytesRead == 0)
return B_ERROR;
if (clone) {
// Clone ancillary data afterwards
} else if (AncillaryDataEntry* entry = fAncillaryData.Head()) {
// Adjust ancillary data entry offsets, respectively attach the ones
// that belong to the read data to the request.
if (AncillaryDataEntry* entry = fAncillaryData.Head()) {
size_t offsetDelta = bytesRead;
while (entry != NULL && offsetDelta > entry->offset) {
// entry data have been read -- add ancillary data to request
@@ -200,7 +227,7 @@ UnixBufferQueue::Read(UnixRequest& request)
readable -= bytesRead;
}
if (fType == UnixFifoType::Datagram) {
if (!clone && fType == UnixFifoType::Datagram) {
fDatagrams.RemoveHead();
if (request.Address() != NULL)
@@ -228,6 +255,18 @@ UnixBufferQueue::Read(UnixRequest& request)
}
}
if (clone) {
AncillaryDataEntry* entry = fAncillaryData.Head();
size_t offsetDelta = request.BytesTransferred();
while (entry != NULL && offsetDelta > entry->offset) {
request.CloneAncillaryData(entry->data);
entry = fAncillaryData.GetNext(entry);
}
if (fType == UnixFifoType::Datagram && request.Address() != NULL)
memcpy(request.Address(), &datagramEntry->address, sizeof(datagramEntry->address));
}
return B_OK;
}
@@ -389,15 +428,15 @@ UnixFifo::Shutdown(uint32 shutdown)
ssize_t
UnixFifo::Read(const iovec* vecs, size_t vecCount,
ancillary_data_container** _ancillaryData,
struct sockaddr_storage* address, bigtime_t timeout)
struct sockaddr_storage* address, bigtime_t timeout, bool peek)
{
TRACE("[%" B_PRId32 "] %p->UnixFifo::Read(%p, %ld, %" B_PRIdBIGTIME ")\n",
find_thread(NULL), this, vecs, vecCount, timeout);
TRACE("[%" B_PRId32 "] %p->UnixFifo::Read(%p, %ld, %" B_PRIdBIGTIME ") %d\n",
find_thread(NULL), this, vecs, vecCount, timeout, peek);
if (IsReadShutdown() && fBuffer.Readable() == 0)
RETURN_ERROR(UNIX_FIFO_SHUTDOWN);
UnixRequest request(vecs, vecCount, NULL, address);
UnixRequest request(vecs, vecCount, NULL, address, peek);
fReaders.Add(&request);
fReadRequested += request.TotalSize();
@@ -37,7 +37,7 @@ class UnixRequest : public DoublyLinkedListLinkImpl<UnixRequest> {
public:
UnixRequest(const iovec* vecs, size_t count,
ancillary_data_container* ancillaryData,
struct sockaddr_storage* address);
struct sockaddr_storage* address, bool clone = false);
off_t TotalSize() const { return fTotalSize; }
off_t BytesTransferred() const { return fBytesTransferred; }
@@ -48,9 +48,11 @@ public:
ancillary_data_container* AncillaryData() const { return fAncillaryData; }
void AddAncillaryData(ancillary_data_container* data);
status_t CloneAncillaryData(ancillary_data_container* data);
void UnsetAncillaryData();
struct sockaddr_storage* Address() const { return fAddress; }
bool IsClone() const { return fClone; }
private:
const iovec* fVecs;
@@ -61,6 +63,7 @@ private:
size_t fVecIndex;
size_t fVecOffset;
struct sockaddr_storage* fAddress;
bool fClone;
};
@@ -134,7 +137,7 @@ public:
ssize_t Read(const iovec* vecs, size_t vecCount,
ancillary_data_container** _ancillaryData,
struct sockaddr_storage* address, bigtime_t timeout);
struct sockaddr_storage* address, bigtime_t timeout, bool peek);
ssize_t Write(const iovec* vecs, size_t vecCount,
ancillary_data_container* ancillaryData,
const struct sockaddr_storage* address, bigtime_t timeout);
@@ -483,7 +483,7 @@ UnixStreamEndpoint::Receive(const iovec* vecs, size_t vecCount,
find_thread(NULL), this, vecs, vecCount);
// TODO: handle MSG_WAITALL
if ((flags & ~(MSG_DONTWAIT | MSG_WAITALL)) != 0)
if ((flags & ~(MSG_DONTWAIT | MSG_PEEK | MSG_WAITALL)) != 0)
return EOPNOTSUPP;
bigtime_t timeout = 0;
@@ -521,7 +521,8 @@ UnixStreamEndpoint::Receive(const iovec* vecs, size_t vecCount,
// unlock endpoint
locker.Unlock();
ssize_t result = fifo->Read(vecs, vecCount, _ancillaryData, NULL, timeout);
ssize_t result = fifo->Read(vecs, vecCount, _ancillaryData, NULL, timeout,
(flags & MSG_PEEK) != 0);
// Notify select()ing writers, if we successfully read anything.
size_t writable = fifo->Writable();
@@ -60,6 +60,21 @@ destroy_scm_rights_descriptors(const ancillary_data_header* header,
}
void
clone_scm_rights_descriptors(const ancillary_data_header* header, void* data)
{
int count = header->len / sizeof(file_descriptor*);
file_descriptor** descriptors = (file_descriptor**)data;
for (int i = 0; i < count; i++) {
if (descriptors[i] != NULL) {
inc_fd_ref_count(descriptors[i]);
inc_fd_open_count(descriptors[i]);
}
}
}
// #pragma mark -
@@ -334,7 +349,7 @@ unix_add_ancillary_data(net_protocol *self, ancillary_data_container *container,
"container\n", find_thread(NULL), count);
error = gStackModule->add_ancillary_data(container, &header,
descriptors, destroy_scm_rights_descriptors, NULL, NULL);
descriptors, destroy_scm_rights_descriptors, clone_scm_rights_descriptors, NULL);
}
// cleanup on error