@@ -82,3 +82,49 @@ impl Deref for FileRef {
8282 self . 0 . deref ( )
8383 }
8484}
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