b9f581f671845efbaff53e13b4575f70e648ac75
[dragonfly.git] / lib / libpuffs / puffs_framebuf.3
1 .\"     $NetBSD: puffs_framebuf.3,v 1.29 2010/04/01 09:57:00 pooka Exp $
2 .\"
3 .\" Copyright (c) 2007 Antti Kantee.  All rights reserved.
4 .\"
5 .\" Redistribution and use in source and binary forms, with or without
6 .\" modification, are permitted provided that the following conditions
7 .\" are met:
8 .\" 1. Redistributions of source code must retain the above copyright
9 .\"    notice, this list of conditions and the following disclaimer.
10 .\" 2. Redistributions in binary form must reproduce the above copyright
11 .\"    notice, this list of conditions and the following disclaimer in the
12 .\"    documentation and/or other materials provided with the distribution.
13 .\"
14 .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 .\" ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 .\" SUCH DAMAGE.
25 .\"
26 .Dd February 5, 2012
27 .Dt PUFFS_FRAMEBUF 3
28 .Os
29 .Sh NAME
30 .Nm puffs_framebuf
31 .Nd buffering and event handling for networked file systems
32 .Sh LIBRARY
33 .Lb libpuffs
34 .Sh SYNOPSIS
35 .In puffs.h
36 .Ft struct puffs_framebuf *
37 .Fn puffs_framebuf_make
38 .Ft void
39 .Fn puffs_framebuf_destroy "struct puffs_framebuf *pufbuf"
40 .Ft void
41 .Fn puffs_framebuf_recycle "struct puffs_framebuf *pufbuf"
42 .Ft int
43 .Fn puffs_framebuf_reserve_space "struct puffs_framebuf *pufbuf" "size_t space"
44 .Ft int
45 .Fo puffs_framebuf_putdata
46 .Fa "struct puffs_framebuf *pufbuf" "const void *data" "size_t dlen"
47 .Fc
48 .Ft int
49 .Fo puffs_framebuf_putdata_atoff
50 .Fa "struct puffs_framebuf *pufbuf" "size_t offset" "const void *data"
51 .Fa "size_t dlen"
52 .Fc
53 .Ft int
54 .Fo puffs_framebuf_getdata
55 .Fa "struct puffs_framebuf *pufbuf" "void *data" "size_t dlen"
56 .Fc
57 .Ft int
58 .Fo puffs_framebuf_getdata_atoff
59 .Fa "struct puffs_framebuf *pufbuf" "size_t offset"
60 .Fa "void *data" "size_t dlen"
61 .Fc
62 .Ft size_t
63 .Fn puffs_framebuf_telloff "struct puffs_framebuf *pufbuf"
64 .Ft size_t
65 .Fn puffs_framebuf_tellsize "struct puffs_framebuf *pufbuf"
66 .Ft size_t
67 .Fn puffs_framebuf_remaining "struct puffs_framebuf *pufbuf"
68 .Ft int
69 .Fn puffs_framebuf_seekset "struct puffs_framebuf *pufbuf" "size_t offset"
70 .Ft int
71 .Fo puffs_framebuf_getwindow
72 .Fa "struct puffs_framebuf *pufbuf" "size_t offset"
73 .Fa "void **winp" "size_t *winlen"
74 .Fc
75 .Ft int
76 .Fo puffs_framev_enqueue_cc
77 .Fa "struct puffs_cc *pcc" "int fd" "struct puffs_framebuf *pufbuf" "int flags"
78 .Fc
79 .Ft void
80 .Fo puffs_framev_cb
81 .Fa "struct puffs_usermount *pu" "int fd" "struct puffs_framebuf *pufbuf"
82 .Fa "void *arg" "int flags"
83 .Fa "int error"
84 .Fc
85 .Ft void
86 .Fo puffs_framev_enqueue_cb
87 .Fa "struct puffs_usermount *pu" "int fd" "struct puffs_framebuf *pufbuf"
88 .Fa "puffs_framebuf_cb fcb" "void *fcb_arg" "int flags"
89 .Fc
90 .Ft void
91 .Fo puffs_framev_enqueue_justsend
92 .Fa "struct puffs_usermount *pu" "int fd" "struct puffs_framebuf *pufbuf"
93 .Fa "int waitreply" "int flags"
94 .Fc
95 .Ft void
96 .Fo puffs_framev_enqueue_directsend
97 .Fa "struct puffs_usermount *pu" "int fd" "struct puffs_framebuf *pufbuf"
98 .Fa "int flags"
99 .Fc
100 .Ft void
101 .Fo puffs_framev_enqueue_directreceive
102 .Fa "struct puffs_usermount *pu" "int fd" "struct puffs_framebuf *pufbuf"
103 .Fa "int flags"
104 .Fc
105 .Ft int
106 .Fo puffs_framev_framebuf_ccpromote
107 .Fa "struct puffs_framebuf *pufbuf" "struct puffs_cc *pcc"
108 .Fc
109 .Ft int
110 .Fn puffs_framev_enqueue_waitevent "struct puffs_cc *pcc" "int fd" "int *what"
111 .Ft int
112 .Fo puffs_framev_readframe_fn
113 .Fa "struct puffs_usermount *pu" "struct puffs_framebuf *pufbuf"
114 .Fa "int fd" "int *done"
115 .Fc
116 .Ft int
117 .Fo puffs_framev_writeframe_fn
118 .Fa "struct puffs_usermount *pu" "struct puffs_framebuf *pufbuf"
119 .Fa "int fd" "int *done"
120 .Fc
121 .Ft int
122 .Fo puffs_framev_cmpframe_fn
123 .Fa "struct puffs_usermount *pu"
124 .Fa "struct puffs_framebuf *cmp1" "struct puffs_framebuf *cmp2" "int *notresp"
125 .Fc
126 .Ft void
127 .Fo puffs_framev_gotframe_fn
128 .Fa "struct puffs_usermount *pu" "struct puffs_framebuf *pufbuf"
129 .Fc
130 .Ft void
131 .Fo puffs_framev_fdnotify_fn
132 .Fa "struct puffs_usermount *pu" "int fd" "int what"
133 .Fc
134 .Ft void
135 .Fo puffs_framev_init
136 .Fa "struct puffs_usermount *pu"
137 .Fa "puffs_framev_readframe_fn rfb" "puffs_framev_writeframe_fn wfb"
138 .Fa "puffs_framev_cmpframe_fn cmpfb" "puffs_framev_gotframe_fn gotfb"
139 .Fa "puffs_framev_fdnotify_fn fdnotfn"
140 .Fc
141 .Ft int
142 .Fn puffs_framev_addfd "struct puffs_usermount *pu" "int fd" "int what"
143 .Ft int
144 .Fn puffs_framev_enablefd "struct puffs_usermount *pu" "int fd" "int what"
145 .Ft int
146 .Fn puffs_framev_disablefd "struct puffs_usermount *pu" "int fd" "int what"
147 .Ft int
148 .Fn puffs_framev_removefd "struct puffs_usermount *pu" "int fd" "int error"
149 .Ft void
150 .Fo puffs_framev_unmountonclose
151 .Fa "struct puffs_usermount *pu" "int fd" "int what"
152 .Fc
153 .Sh DESCRIPTION
154 The
155 .Nm
156 routines provide buffering and an event loop structured around the
157 buffers.
158 It operates on top of the puffs continuation framework,
159 .Xr puffs_cc 3 ,
160 and multiplexes execution automatically to an instance whenever
161 one is runnable.
162 .Pp
163 The file system is entered in three different ways:
164 .Bl -bullet -offset indent
165 .It
166 An event arrives from the kernel and the
167 .Xr puffs_ops 3
168 callbacks are called to start processing the event.
169 .It
170 A file system which has sent out a request receives a response.
171 Execution is resumed from the place where the file system yielded.
172 .It
173 A request from a peer arrives.
174 A request is an incoming PDU which is not a response to any outstanding
175 request.
176 .El
177 .Pp
178 .Nm
179 is used by defining various callbacks and providing I/O descriptors,
180 which are then monitored for activity by the library.
181 A descriptor, when present, can be either enabled or disabled for
182 input and output.
183 If a descriptor is not enabled for a certain direction, the callbacks
184 will not be called even if there were activity on the descriptor.
185 For example, even if a network socket has been added and there is
186 input data in the socket buffer, the read callback will be called
187 only if the socket has been enabled for reading.
188 .Pp
189 File descriptors are treated like sockets: they have two sides, a read
190 side and a write side.
191 The framework determines that one side of the descriptor has been
192 closed if the supplied I/O callbacks return an error or if the I/O
193 multiplexing call says a side has been closed.
194 It is still possible, from the framework perspective, to write to a
195 file descriptor whose read side is closed.
196 However, it is not possible to wait for a response on such a file
197 descriptor.
198 Conversely, it is possible to read responses from a descriptor whose
199 write side is closed.
200 It should be stressed that the implementation underlying the file
201 descriptor might not support this.
202 .Pp
203 The following callbacks can be defined, cf.
204 .Fn puffs_framev_init ,
205 and all are optional.
206 None of them should block, because this would cause the entire file server
207 to block.
208 One option is to make the descriptors non-blocking before adding them.
209 .Bl -tag -width "xfdnotfnx"
210 .It rfb
211 Read a frame from the file descriptor onto the specified buffer.
212 .It wfb
213 Write a frame from the specified buffer into the file descriptor.
214 .It cmpfb
215 Identify if a buffer is the response to the specified buffer.
216 .It gotfb
217 Called iff no outstanding request matches the incoming frame.
218 In other words, this is called when we receive a request from a peer.
219 .It fdnotfn
220 Receive notifications about a change-of-state in a file descriptor's
221 status.
222 .El
223 .Pp
224 Better descriptions for each callback are given below.
225 .Pp
226 The buffers of
227 .Nm
228 provide automatic memory management of buffers for the file servers.
229 They provide a cursor to the current buffer offset.
230 Reading or writing data through the normal routines will advance that cursor.
231 Additionally, the buffer size is provided to the user.
232 It represents the maximum offset where data was written.
233 .Pp
234 Generally the write functions will fail if the cannot allocate enough
235 memory to satisfy the buffer length requirements.
236 Read functions will fail if the amount of data written to the buffer
237 is not large enough to satisfy the read.
238 .Bl -tag -width xxxx
239 .It Fn puffs_framebuf_make
240 Create a buffer.
241 Return the address of the buffer or
242 .Dv NULL
243 in case no memory was available.
244 .It Fn puffs_framebuf_destroy pufbuf
245 Free memory used by buffer.
246 .It Fn puffs_framebuf_recycle pufbuf
247 Reset offsets so that buffer can be reused.
248 Does not free memory or reallocate memory.
249 .It Fn puffs_framebuf_reserve_space pufbuf space
250 Make sure that the buffer has
251 .Ar space
252 bytes of available memory starting from the current offset.
253 This is not strictly necessary, but can be used for optimizations
254 where it is known in advance how much memory will be required.
255 .It Fn puffs_framebuf_putdata pufbuf data dlen
256 Write
257 .Ar dlen
258 amount of data from the address
259 .Ar data
260 into the buffer.
261 Moves the offset cursor forward
262 .Ar dlen
263 bytes.
264 .It Fn puffs_framebuf_putdata_atoff pufbuf offset data dlen
265 Like
266 .Fn puffs_framebuf_putdata ,
267 except writes data at buffer offset
268 .Ar offset .
269 It is legal to write past the current end of the buffer.
270 Does NOT modify the current offset cursor.
271 .It Fn puffs_framebuf_getdata pufbuf data dlen
272 Read
273 .Ar dlen
274 bytes of data from the buffer into
275 .Ar data .
276 Advances the offset cursor.
277 .It Fn puffs_framebuf_getdata_atoff pufbuf offset data dlen
278 Read data from buffer position
279 .Ar offset .
280 Does NOT modify the offset cursor.
281 .It Fn puffs_framebuf_telloff pufbuf
282 Return the offset into the buffer.
283 .It Fn puffs_framebuf_tellsize pufbuf
284 Return the maximum offset where data has been written, i.e. buffer size.
285 .It Fn puffs_framebuf_remaining pufbuf
286 Distance from current offset to the end of the buffer, i.e. size-offset.
287 .It Fn puffs_framebuf_seekset pufbuf offset
288 Set the offset cursor to the position
289 .Ar offset .
290 This does NOT modify the buffer size, but reserves at least
291 enough memory memory for a write to
292 .Ar offset
293 and will fail if memory cannot be allocated.
294 .It Fn puffs_framebuf_getwindow pufbuf offset winp winlen
295 Get a direct memory window into the buffer starting from
296 .Ar offset .
297 The maximum mapped window size will be
298 .Ar winlen
299 bytes, but this routine might return a smaller window and the caller
300 should always check the actual mapped size after the call.
301 The window is returned in
302 .Ar winp .
303 This function not modify the buffer offset, but it DOES set the buffer
304 size to
305 .Ar offset +
306 .Ar winlen
307 in case that value is greater than the current size.
308 The window is valid until the next until the next
309 .Fn puffs_framebuf
310 call operating on the buffer in question.
311 .It Fn puffs_framev_enqueue_cc pcc fd pufbuf flags
312 Add the buffer
313 .Ar pufbuf
314 to outgoing queue of descriptor
315 .Ar fd
316 and yield with the continuation
317 .Ar pcc .
318 Execution is resumed once a response is received.
319 Returns 0 if the buffer was successfully enqueued (not necessarily
320 delivered) and non-zero to signal a non-recoverable error.
321 .Pp
322 Usually the buffer is placed at the end of the output queue.
323 However, if
324 .Ar flags
325 contains
326 .Dv PUFFS_FBQUEUE_URGENT ,
327 .Ar pufbuf
328 is placed in the front of the queue to be sent immediately after
329 the current PDU (if any) has been sent.
330 .It Fn puffs_framev_enqueue_cb pu fd pufbuf fcb fcb_arg flags
331 Enqueue the buffer
332 .Ar pufbuf
333 for outgoing data and immediately return.
334 Once a response arrives, the callback
335 .Fn fcb
336 will be called with the argument
337 .Ar fcb_arg .
338 The callback function
339 .Fn fcb
340 is responsible for freeing the buffer.
341 Returns 0 if the buffer was successfully enqueued (not necessarily
342 delivered) and non-zero to signal a non-recoverable error.
343 .Pp
344 See
345 .Fn puffs_framev_enqueue_cc
346 for
347 .Ar flags .
348 .It Fn puffs_framev_cb pu pufbuf arg error
349 Callback function.
350 Called when a response to a specific request arrives from the server.
351 If
352 .Ar error
353 is non-zero, the framework was unable to obtain a response and the
354 function should not examine the contents of
355 .Ar pufbuf ,
356 only do resource cleanup.
357 May not block.
358 .It Fn puffs_framev_enqueue_justsend pu fd pufbuf waitreply flags
359 Enqueue the buffer
360 .Ar pufbuf
361 for outgoing traffic and immediately return.
362 The parameter
363 .Ar waitreply
364 can be used to control if the buffer is to be freed immediately after
365 sending of if a response is expected and the buffer should be freed
366 only after the response arrives (receiving an unexpected message from
367 the server is treated as an error).
368 Returns 0 if the buffer was successfully enqueued (not necessarily
369 delivered) and non-zero to signal a non-recoverable error.
370 .Pp
371 See
372 .Fn puffs_framev_enqueue_cc
373 for
374 .Ar flags .
375 .It Fn puffs_framev_enqueue_directsend pcc fd pufbuf flags
376 Acts like
377 .Fn puffs_framev_enqueue_justsend
378 with the exception that the call yields until the frame has been sent.
379 As opposed to
380 .Fn puffs_framev_enqueue_cc ,
381 the routine does not wait for input, but returns immediately after
382 sending the frame.
383 .Pp
384 See
385 .Fn puffs_framev_enqueue_cc
386 for
387 .Ar flags .
388 .It Fn puffs_framev_enqueue_directreceive pcc fd pufbuf flags
389 Receive data into
390 .Ar pufbuf .
391 This routine yields until a complete frame has been read into
392 the buffer by the readframe routine.
393 .Pp
394 See
395 .Fn puffs_framev_enqueue_cc
396 for
397 .Ar flags .
398 .It Fn puffs_framev_framebuf_ccpromote pufbuf pcc
399 Promote the framebuffer
400 .Ar pufbuf
401 sent with
402 .Fn puffs_framev_enqueue_cb
403 or
404 .Fn puffs_framev_enqueue_justsend
405 to a wait using
406 .Ar pcc
407 and yield until the result arrives.
408 The response from the file server for
409 .Ar pufbuf
410 must not yet have arrived.
411 If sent with
412 .Fn puffs_framev_enqueue_justsend ,
413 the call must be expecting a response.
414 .It Fn puffs_framev_enqueue_waitevent pcc fd what
415 Waits for an event in
416 .Ar what
417 to happen on file descriptor
418 .Ar fd .
419 The events which happened are returned back in
420 .Ar what .
421 The possible events are
422 .Dv PUFFS_FBIO_READ ,
423 .Dv PUFFS_FBIO_WRITE ,
424 and
425 .Dv PUFFS_FBIO_ERROR ,
426 specifying read, write and error conditions, respectively.
427 Error is always checked.
428 .Pp
429 This call does not depend on if the events were previously enabled on
430 the file descriptor - the specified events are always checked
431 regardless.
432 .Pp
433 There is currently no other way to cancel or timeout a call except by
434 removing the file descriptor in question.
435 This may change in the future.
436 .It Fn puffs_framev_readframe_fn pu pufbuf fd done
437 Callback function.
438 Read at most one frame from file descriptor
439 .Ar fd
440 into the buffer
441 .Ar pufbuf .
442 If a complete frame is read, the value pointed to by
443 .Ar done
444 must be set to 1.
445 This function should return 0 on success (even if a complete frame was not
446 yet read) and a non-zero
447 .Er errno
448 to signal a fatal error.
449 In case a fatal error is returned, the read side of the file descriptor
450 is marked closed.
451 This routine will be called with the same buffer argument until a
452 complete frame has been read.
453 May not block.
454 .It Fn puffs_framev_writeframe_fn pu pufbuf fd done
455 Write the frame contained in
456 .Ar pufbuf
457 to the file descriptor
458 .Ar fd .
459 In case the entire frame is successfully written,
460 .Ar *done
461 should be set to 1.
462 This function should return 0 on success (even if a complete frame was not
463 yet written) and a non-zero
464 .Er errno
465 to signal a fatal error.
466 In case a fatal error is returned, the write side of the file descriptor
467 is marked closed.
468 This routine will be called with the same buffer argument until the
469 complete frame has been written.
470 May not block.
471 .Pp
472 It is a good idea to make sure that this function can handle a possible
473 .Dv SIGPIPE
474 caused by a closed connection.
475 For example, the file server can opt to trap
476 .Dv SIGPIPE
477 or, if writing to a socket, call
478 .Fn send
479 with the flag
480 .Dv MSG_NOSIGNAL
481 instead of using
482 .Fn write .
483 .It Fn puffs_framev_cmpframe_fn pu pufbuf_cmp1 pufbuf_cmp2 notresp
484 Compare the file system internal request tags in
485 .Ar pufbuf_cmp1
486 and
487 .Ar pufbuf_cmp2 .
488 Should return 0 if the tags are equal, 1 if first buffer's tag is
489 greater than the second and \-1 if it is smaller.
490 The definitions "greater" and "smaller" are used transparently by
491 the library, e.g. like
492 .Xr qsort 3 .
493 If it can be determined from
494 .Ar pufbuf_cmp1
495 that it is not a response to any outstanding request,
496 .Ar notresp
497 should be set to non-zero.
498 This will cause
499 .Nm
500 to skip the test of the buffer against the rest of the outstanding
501 request.
502 May not block.
503 .It Fn puffs_framev_gotframe_fn pu pufbuf
504 Called when no outstanding request matches an incoming frame.
505 The ownership of
506 .Ar pufbuf
507 is transferred to the called function and must be destroyed once
508 processing is over.
509 May not block.
510 .It Fn puffs_framev_fdnotify_fn pu fd what
511 Is called when the read or write side of the file descriptor
512 .Ar fd
513 is closed.
514 It is called once for each side, the bitmask parameter
515 .Ar what
516 specified what is currently closed:
517 .Dv PUFFS_FBIO_READ
518 and
519 .Dv PUFFS_FBIO_WRITE
520 for read and write, respectively.
521 .It Fn puffs_framev_init pu rfb wfb cmpfb gotfb fdnotfn
522 Initializes the given callbacks to the system.
523 They will be used when
524 .Fn puffs_mainloop
525 is called.
526 The framework provides the routines
527 .Fn puffs_framev_removeonclose
528 and
529 .Fn puffs_framev_unmountonclose ,
530 which can be given as
531 .Ar fdnotfn .
532 The first one removes the file descriptor once both sides are closed
533 while the second one unmounts the file system and exits the mainloop.
534 .It Fn puffs_framev_addfd pu fd what
535 Add file descriptor
536 .Ar fd
537 to be handled by the framework.
538 It is legal to add a file descriptor either before calling
539 .Fn puffs_mainloop
540 or at time when running.
541 The parameter
542 .Ar what
543 controls enabling of input and output events and can be a bitwise
544 combination of
545 .Dv PUFFS_FBIO_READ
546 and
547 .Dv PUFFS_FBIO_WRITE .
548 If not specified, the descriptor will be in a disabled state.
549 .It Fn puffs_framev_enablefd pu fd what
550 Enable events of type
551 .Ar what
552 for file descriptor
553 .Ar fd .
554 .It Fn puffs_framev_disablefd pu fd what
555 Disable events of type
556 .Ar what
557 for file descriptor
558 .Ar fd .
559 .It Fn puffs_framev_removefd pu fd error
560 Remove file descriptor
561 .Ar fd
562 from the list of descriptors handled by the framework.
563 Removing a file descriptor causes all operations blocked either on
564 output or input to be released with the error value
565 .Ar error .
566 In case 0 is supplied as this parameter,
567 .Er ECONNRESET
568 is used.
569 .Pp
570 The file system
571 .Em must
572 explicitly remove each fd it has added.
573 A good place to do this is
574 .Fn puffs_framev_fdnotify_fn
575 or
576 .Fn puffs_node_reclaim ,
577 depending a little on the structure of the file system.
578 .It Fn puffs_framev_unmountonclose pu fd what
579 This is library provided convenience routine for
580 .Fn puffs_framev_fdnotify_fn .
581 It unmounts the file system when both the read and write side are
582 closed.
583 It is useful for file systems such as
584 .Xr mount_psshfs 8
585 which depend on a single connection.
586 .El
587 .Sh CODE REFERENCES
588 The current user of
589 .Nm
590 in the tree is
591 .Xr mount_psshfs 8 .
592 .\"and
593 .\".Xr mount_9p 8 .
594 See
595 .Pa src/usr.sbin/puffs/mount_psshfs .
596 .\"and
597 .\".Pa src/usr.sbin/puffs/mount_9p
598 for the respective usage examples.
599 .Sh RETURN VALUES
600 These functions generally return \-1 to signal error and set
601 .Er errno
602 to indicate the type of error.
603 .Sh SEE ALSO
604 .Xr puffs 3 ,
605 .Xr puffs_cc 3 ,
606 .Xr puffs_ops 3
607 .Rs
608 .%A Antti Kantee
609 .%D September 2007
610 .%I Helsinki University of Technology
611 .%R Tech Report TKK-TKO-B157
612 .%T Using puffs for Implementing Client-Server Distributed File Systems
613 .Re
614 .Rs
615 .%A Antti Kantee
616 .%D March 2008
617 .%J Proceedings of AsiaBSDCon 2008
618 .%P pp. 55-70
619 .%T Send and Receive of File System Protocols: Userspace Approach With puffs
620 .Re