2 * Copyright 2005, Ingo Weinhold <bonefish@cs.tu-berlin.de>.
3 * All rights reserved. Distributed under the terms of the MIT License.
6 #include <boot/net/RemoteDisk.h>
14 #include <SupportDefs.h>
16 #include <boot/net/UDP.h>
19 static const bigtime_t kRequestTimeout
= 100000LL;
25 #if __BYTE_ORDER == __LITTLE_ENDIAN
28 uint64_t swap_uint64(uint64_t data
)
30 return ((data
& 0xff) << 56)
31 | ((data
& 0xff00) << 40)
32 | ((data
& 0xff0000) << 24)
33 | ((data
& 0xff000000) << 8)
34 | ((data
>> 8) & 0xff000000)
35 | ((data
>> 24) & 0xff0000)
36 | ((data
>> 40) & 0xff00)
37 | ((data
>> 56) & 0xff);
40 #define host_to_net64(data) swap_uint64(data)
41 #define net_to_host64(data) swap_uint64(data)
45 #if __BYTE_ORDER == __BIG_ENDIAN
46 #define host_to_net64(data) (data)
47 #define net_to_host64(data) (data)
52 #define htonll(data) host_to_net64(data)
53 #define ntohll(data) net_to_host64(data)
57 RemoteDisk::RemoteDisk()
58 : fServerAddress(INADDR_ANY
),
68 RemoteDisk::~RemoteDisk()
76 RemoteDisk::Init(ip_addr_t serverAddress
, uint16 serverPort
, off_t imageSize
)
78 fServerAddress
= serverAddress
;
79 fServerPort
= serverPort
;
80 fImageSize
= imageSize
;
82 // create and bind socket
83 fSocket
= new(nothrow
) UDPSocket
;
87 status_t error
= fSocket
->Bind(INADDR_ANY
, 6666);
96 RemoteDisk::ReadAt(void */
*cookie*/
, off_t pos
, void *_buffer
,
102 uint8
*buffer
= (uint8
*)_buffer
;
103 if (!buffer
|| pos
< 0)
109 // Check whether the current packet already contains the beginning of the
111 ssize_t bytesRead
= _ReadFromPacket(pos
, buffer
, bufferSize
);
113 // If there still remains something to be read, we need to get it from the
115 status_t error
= B_OK
;
116 while (bufferSize
> 0) {
118 remote_disk_header request
;
119 request
.offset
= htonll(pos
);
120 uint32 toRead
= min_c(bufferSize
, REMOTE_DISK_BLOCK_SIZE
);
121 request
.size
= htons(toRead
);
122 request
.command
= REMOTE_DISK_READ_REQUEST
;
126 error
= _SendRequest(&request
, sizeof(request
), REMOTE_DISK_READ_REPLY
,
132 int16 packetSize
= ntohs(((remote_disk_header
*)packet
->Data())->size
);
133 if (packetSize
< 0) {
134 if (packetSize
== REMOTE_DISK_IO_ERROR
)
136 else if (packetSize
== REMOTE_DISK_BAD_REQUEST
)
141 // make the reply packet the current packet
145 // read from the packet
146 size_t packetBytesRead
= _ReadFromPacket(pos
, buffer
, bufferSize
);
147 if (packetBytesRead
== 0)
149 bytesRead
+= packetBytesRead
;
152 // only return an error, when we were not able to read anything at all
153 return (bytesRead
== 0 ? error
: bytesRead
);
158 RemoteDisk::WriteAt(void */
*cookie*/
, off_t pos
, const void *buffer
,
161 // Not needed in the boot loader.
162 return B_PERMISSION_DENIED
;
167 RemoteDisk::GetName(char *nameBuffer
, size_t bufferSize
) const
172 snprintf(nameBuffer
, bufferSize
,
173 "RemoteDisk:%" B_PRIu32
".%" B_PRIu32
".%" B_PRIu32
".%" B_PRIu32
":%hd",
174 (fServerAddress
>> 24) & 0xff, (fServerAddress
>> 16) & 0xff,
175 (fServerAddress
>> 8) & 0xff, fServerAddress
& 0xff, fServerPort
);
182 RemoteDisk::Size() const
188 RemoteDisk::ServerIPAddress() const
190 return fServerAddress
;
194 RemoteDisk::ServerPort() const
201 RemoteDisk::FindAnyRemoteDisk()
203 // create a socket and bind it
205 status_t error
= socket
.Bind(INADDR_ANY
, 6665);
207 printf("RemoteDisk::GetAnyRemoteDisk(): Failed to bind socket.\n");
212 remote_disk_header request
;
213 request
.command
= REMOTE_DISK_HELLO_REQUEST
;
217 error
= _SendRequest(&socket
, INADDR_BROADCAST
, REMOTE_DISK_SERVER_PORT
,
218 &request
, sizeof(request
), REMOTE_DISK_HELLO_REPLY
, &packet
);
220 printf("RemoteDisk::GetAnyRemoteDisk(): Got no server reply.\n");
223 remote_disk_header
*reply
= (remote_disk_header
*)packet
->Data();
225 // create a RemoteDisk object
226 RemoteDisk
*remoteDisk
= new(nothrow
) RemoteDisk
;
228 error
= remoteDisk
->Init(packet
->SourceAddress(), ntohs(reply
->port
),
229 ntohll(reply
->offset
));
243 RemoteDisk::_ReadFromPacket(off_t
&pos
, uint8
*&buffer
, size_t &bufferSize
)
248 remote_disk_header
*header
= (remote_disk_header
*)fPacket
->Data();
249 uint64 packetOffset
= ntohll(header
->offset
);
250 uint32 packetSize
= ntohs(header
->size
);
251 if (packetOffset
> (uint64
)pos
|| packetOffset
+ packetSize
<= (uint64
)pos
)
254 // we do indeed have some bytes already
255 size_t toCopy
= size_t(packetOffset
+ packetSize
- (uint64
)pos
);
256 if (toCopy
> bufferSize
)
258 memcpy(buffer
, header
->data
+ (pos
- packetOffset
), toCopy
);
262 bufferSize
-= toCopy
;
268 RemoteDisk::_SendRequest(UDPSocket
*socket
, ip_addr_t serverAddress
,
269 uint16 serverPort
, remote_disk_header
*request
, size_t size
,
270 uint8 expectedReply
, UDPPacket
**_packet
)
272 request
->port
= htons(socket
->Port());
274 // try sending the request 3 times at most
275 for (int i
= 0; i
< 3; i
++) {
277 status_t error
= socket
->Send(serverAddress
, serverPort
, request
, size
);
282 bigtime_t timeout
= system_time() + kRequestTimeout
;
285 error
= socket
->Receive(&packet
, timeout
- system_time());
287 // got something; check, if it is looks good
288 if (packet
->DataSize() >= sizeof(remote_disk_header
)) {
289 remote_disk_header
*reply
290 = (remote_disk_header
*)packet
->Data();
291 if (reply
->request_id
== request
->request_id
292 && reply
->command
== expectedReply
) {
300 } else if (error
!= B_TIMED_OUT
&& error
!= B_WOULD_BLOCK
)
303 } while (timeout
> system_time());
312 RemoteDisk::_SendRequest(remote_disk_header
*request
, size_t size
,
313 uint8 expectedReply
, UDPPacket
**packet
)
315 request
->request_id
= fRequestID
++;
316 return _SendRequest(fSocket
, fServerAddress
, fServerPort
, request
, size
,
317 expectedReply
, packet
);