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", TRACE("[%" B_PRId32 "] %p->UnixDatagramEndpoint::Receive()\n",
find_thread(NULL), this); find_thread(NULL), this);
if ((flags & ~(MSG_DONTWAIT)) != 0) if ((flags & ~(MSG_DONTWAIT | MSG_PEEK)) != 0)
return EOPNOTSUPP; return EOPNOTSUPP;
bigtime_t timeout = 0; bigtime_t timeout = 0;
@@ -370,7 +370,8 @@ UnixDatagramEndpoint::Receive(const iovec* vecs, size_t vecCount,
struct sockaddr_storage addressStorage; 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. // Notify select()ing writers, if we successfully read anything.
size_t writable = fifo->Writable(); size_t writable = fifo->Writable();
@@ -25,7 +25,7 @@
UnixRequest::UnixRequest(const iovec* vecs, size_t count, UnixRequest::UnixRequest(const iovec* vecs, size_t count,
ancillary_data_container* ancillaryData, ancillary_data_container* ancillaryData,
struct sockaddr_storage* address) struct sockaddr_storage* address, bool clone)
: :
fVecs(vecs), fVecs(vecs),
fVecCount(count), fVecCount(count),
@@ -34,7 +34,8 @@ UnixRequest::UnixRequest(const iovec* vecs, size_t count,
fBytesTransferred(0), fBytesTransferred(0),
fVecIndex(0), fVecIndex(0),
fVecOffset(0), fVecOffset(0),
fAddress(address) fAddress(address),
fClone(clone)
{ {
for (size_t i = 0; i < fVecCount; i++) for (size_t i = 0; i < fVecCount; i++)
fTotalSize += fVecs[i].iov_len; 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 // #pragma mark - UnixBufferQueue
@@ -147,6 +161,7 @@ UnixBufferQueue::Read(UnixRequest& request)
bool user = gStackModule->is_syscall(); bool user = gStackModule->is_syscall();
size_t readable = Readable(); size_t readable = Readable();
const bool clone = request.IsClone();
void* data; void* data;
size_t size; size_t size;
@@ -168,19 +183,31 @@ UnixBufferQueue::Read(UnixRequest& request)
size = readable; size = readable;
ssize_t bytesRead; ssize_t bytesRead;
if (user) if (user) {
bytesRead = ring_buffer_user_read(fBuffer, (uint8*)data, size); if (clone) {
else bytesRead = ring_buffer_user_peek(fBuffer, request.BytesTransferred(),
bytesRead = ring_buffer_read(fBuffer, (uint8*)data, size); (uint8*)data, size);
} else {
bytesRead = ring_buffer_user_read(fBuffer, (uint8*)data, size);
}
} 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) if (bytesRead < 0)
return bytesRead; return bytesRead;
if (bytesRead == 0) if (bytesRead == 0)
return B_ERROR; return B_ERROR;
// Adjust ancillary data entry offsets, respectively attach the ones if (clone) {
// that belong to the read data to the request. // Clone ancillary data afterwards
if (AncillaryDataEntry* entry = fAncillaryData.Head()) { } else if (AncillaryDataEntry* entry = fAncillaryData.Head()) {
// Adjust ancillary data entry offsets, respectively attach the ones
// that belong to the read data to the request.
size_t offsetDelta = bytesRead; size_t offsetDelta = bytesRead;
while (entry != NULL && offsetDelta > entry->offset) { while (entry != NULL && offsetDelta > entry->offset) {
// entry data have been read -- add ancillary data to request // entry data have been read -- add ancillary data to request
@@ -200,7 +227,7 @@ UnixBufferQueue::Read(UnixRequest& request)
readable -= bytesRead; readable -= bytesRead;
} }
if (fType == UnixFifoType::Datagram) { if (!clone && fType == UnixFifoType::Datagram) {
fDatagrams.RemoveHead(); fDatagrams.RemoveHead();
if (request.Address() != NULL) 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; return B_OK;
} }
@@ -389,15 +428,15 @@ UnixFifo::Shutdown(uint32 shutdown)
ssize_t ssize_t
UnixFifo::Read(const iovec* vecs, size_t vecCount, UnixFifo::Read(const iovec* vecs, size_t vecCount,
ancillary_data_container** _ancillaryData, 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", TRACE("[%" B_PRId32 "] %p->UnixFifo::Read(%p, %ld, %" B_PRIdBIGTIME ") %d\n",
find_thread(NULL), this, vecs, vecCount, timeout); find_thread(NULL), this, vecs, vecCount, timeout, peek);
if (IsReadShutdown() && fBuffer.Readable() == 0) if (IsReadShutdown() && fBuffer.Readable() == 0)
RETURN_ERROR(UNIX_FIFO_SHUTDOWN); RETURN_ERROR(UNIX_FIFO_SHUTDOWN);
UnixRequest request(vecs, vecCount, NULL, address); UnixRequest request(vecs, vecCount, NULL, address, peek);
fReaders.Add(&request); fReaders.Add(&request);
fReadRequested += request.TotalSize(); fReadRequested += request.TotalSize();
@@ -37,7 +37,7 @@ class UnixRequest : public DoublyLinkedListLinkImpl<UnixRequest> {
public: public:
UnixRequest(const iovec* vecs, size_t count, UnixRequest(const iovec* vecs, size_t count,
ancillary_data_container* ancillaryData, ancillary_data_container* ancillaryData,
struct sockaddr_storage* address); struct sockaddr_storage* address, bool clone = false);
off_t TotalSize() const { return fTotalSize; } off_t TotalSize() const { return fTotalSize; }
off_t BytesTransferred() const { return fBytesTransferred; } off_t BytesTransferred() const { return fBytesTransferred; }
@@ -48,9 +48,11 @@ public:
ancillary_data_container* AncillaryData() const { return fAncillaryData; } ancillary_data_container* AncillaryData() const { return fAncillaryData; }
void AddAncillaryData(ancillary_data_container* data); void AddAncillaryData(ancillary_data_container* data);
status_t CloneAncillaryData(ancillary_data_container* data);
void UnsetAncillaryData(); void UnsetAncillaryData();
struct sockaddr_storage* Address() const { return fAddress; } struct sockaddr_storage* Address() const { return fAddress; }
bool IsClone() const { return fClone; }
private: private:
const iovec* fVecs; const iovec* fVecs;
@@ -61,6 +63,7 @@ private:
size_t fVecIndex; size_t fVecIndex;
size_t fVecOffset; size_t fVecOffset;
struct sockaddr_storage* fAddress; struct sockaddr_storage* fAddress;
bool fClone;
}; };
@@ -134,7 +137,7 @@ public:
ssize_t Read(const iovec* vecs, size_t vecCount, ssize_t Read(const iovec* vecs, size_t vecCount,
ancillary_data_container** _ancillaryData, 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, ssize_t Write(const iovec* vecs, size_t vecCount,
ancillary_data_container* ancillaryData, ancillary_data_container* ancillaryData,
const struct sockaddr_storage* address, bigtime_t timeout); 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); find_thread(NULL), this, vecs, vecCount);
// TODO: handle MSG_WAITALL // TODO: handle MSG_WAITALL
if ((flags & ~(MSG_DONTWAIT | MSG_WAITALL)) != 0) if ((flags & ~(MSG_DONTWAIT | MSG_PEEK | MSG_WAITALL)) != 0)
return EOPNOTSUPP; return EOPNOTSUPP;
bigtime_t timeout = 0; bigtime_t timeout = 0;
@@ -521,7 +521,8 @@ UnixStreamEndpoint::Receive(const iovec* vecs, size_t vecCount,
// unlock endpoint // unlock endpoint
locker.Unlock(); 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. // Notify select()ing writers, if we successfully read anything.
size_t writable = fifo->Writable(); 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 - // #pragma mark -
@@ -334,7 +349,7 @@ unix_add_ancillary_data(net_protocol *self, ancillary_data_container *container,
"container\n", find_thread(NULL), count); "container\n", find_thread(NULL), count);
error = gStackModule->add_ancillary_data(container, &header, 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 // cleanup on error