@@ -82,3 +82,49 @@ impl Deref for FileRef {
82
82
self . 0 . deref ( )
83
83
}
84
84
}
85
+
86
+ /// A file descriptor reservation.
87
+ ///
88
+ /// This allows the creation of a file descriptor in two steps: first, we reserve a slot for it,
89
+ /// then we commit or drop the reservation. The first step may fail (e.g., the current process ran
90
+ /// out of available slots), but commit and drop never fail (and are mutually exclusive).
91
+ pub struct FileDescriptorReservation {
92
+ fd : u32 ,
93
+ }
94
+
95
+ impl FileDescriptorReservation {
96
+ /// Creates a new file descriptor reservation.
97
+ pub fn new ( flags : u32 ) -> Result < Self > {
98
+ let fd = unsafe { bindings:: get_unused_fd_flags ( flags) } ;
99
+ if fd < 0 {
100
+ return Err ( Error :: from_kernel_errno ( fd) ) ;
101
+ }
102
+ Ok ( Self { fd : fd as _ } )
103
+ }
104
+
105
+ /// Returns the file descriptor number that was reserved.
106
+ pub fn reserved_fd ( & self ) -> u32 {
107
+ self . fd
108
+ }
109
+
110
+ /// Commits the reservation.
111
+ ///
112
+ /// The previously reserved file descriptor is bound to `file`.
113
+ pub fn commit ( self , file : File ) {
114
+ // SAFETY: `self.fd` was previously returned by `get_unused_fd_flags`, and `file.ptr` is
115
+ // guaranteed to have an owned ref count by its type invariants.
116
+ unsafe { bindings:: fd_install ( self . fd , file. ptr ) } ;
117
+
118
+ // `fd_install` consumes both the file descriptor and the file reference, so we cannot run
119
+ // the destructors.
120
+ core:: mem:: forget ( self ) ;
121
+ core:: mem:: forget ( file) ;
122
+ }
123
+ }
124
+
125
+ impl Drop for FileDescriptorReservation {
126
+ fn drop ( & mut self ) {
127
+ // SAFETY: `self.fd` was returned by a previous call to `get_unused_fd_flags`.
128
+ unsafe { bindings:: put_unused_fd ( self . fd ) } ;
129
+ }
130
+ }
0 commit comments