1
1
// SPDX-License-Identifier: GPL-2.0
2
2
3
- use alloc:: sync:: Arc ;
4
- use core:: sync:: atomic:: { AtomicBool , Ordering } ;
3
+ use alloc:: { boxed:: Box , sync:: Arc } ;
4
+ use core:: {
5
+ pin:: Pin ,
6
+ sync:: atomic:: { AtomicBool , Ordering } ,
7
+ } ;
5
8
use kernel:: {
6
- io_buffer:: IoBufferWriter , linked_list:: Links , prelude:: * , sync:: Ref ,
7
- user_ptr:: UserSlicePtrWriter , ScopeGuard ,
9
+ bindings,
10
+ file:: { File , FileDescriptorReservation } ,
11
+ io_buffer:: IoBufferWriter ,
12
+ linked_list:: List ,
13
+ linked_list:: { GetLinks , Links } ,
14
+ prelude:: * ,
15
+ sync:: { Ref , SpinLock } ,
16
+ user_ptr:: UserSlicePtrWriter ,
17
+ Error , ScopeGuard ,
8
18
} ;
9
19
10
20
use crate :: {
@@ -16,7 +26,12 @@ use crate::{
16
26
DeliverToRead , Either ,
17
27
} ;
18
28
29
+ struct TransactionInner {
30
+ file_list : List < Box < FileInfo > > ,
31
+ }
32
+
19
33
pub ( crate ) struct Transaction {
34
+ inner : SpinLock < TransactionInner > ,
20
35
// TODO: Node should be released when the buffer is released.
21
36
node_ref : Option < NodeRef > ,
22
37
stack_next : Option < Arc < Transaction > > ,
@@ -37,13 +52,16 @@ impl Transaction {
37
52
stack_next : Option < Arc < Transaction > > ,
38
53
from : & Arc < Thread > ,
39
54
tr : & BinderTransactionData ,
40
- ) -> BinderResult < Self > {
55
+ ) -> BinderResult < Arc < Self > > {
41
56
let allow_fds = node_ref. node . flags & FLAT_BINDER_FLAG_ACCEPTS_FDS != 0 ;
42
57
let to = node_ref. node . owner . clone ( ) ;
43
- let alloc = from. copy_transaction_data ( & to, tr, allow_fds) ?;
58
+ let mut alloc = from. copy_transaction_data ( & to, tr, allow_fds) ?;
44
59
let data_address = alloc. ptr ;
60
+ let file_list = alloc. take_file_list ( ) ;
45
61
alloc. keep_alive ( ) ;
46
- Ok ( Self {
62
+ let mut tr = Arc :: try_new ( Self {
63
+ // SAFETY: `spinlock_init` is called below.
64
+ inner : unsafe { SpinLock :: new ( TransactionInner { file_list } ) } ,
47
65
node_ref : Some ( node_ref) ,
48
66
stack_next,
49
67
from : from. clone ( ) ,
@@ -55,19 +73,28 @@ impl Transaction {
55
73
offsets_size : tr. offsets_size as _ ,
56
74
links : Links :: new ( ) ,
57
75
free_allocation : AtomicBool :: new ( true ) ,
58
- } )
76
+ } ) ?;
77
+
78
+ let mut_tr = Arc :: get_mut ( & mut tr) . ok_or ( Error :: EINVAL ) ?;
79
+
80
+ // SAFETY: `inner` is pinned behind `Arc`.
81
+ kernel:: spinlock_init!( Pin :: new_unchecked( & mut_tr. inner) , "Transaction::inner" ) ;
82
+ Ok ( tr)
59
83
}
60
84
61
85
pub ( crate ) fn new_reply (
62
86
from : & Arc < Thread > ,
63
87
to : Ref < Process > ,
64
88
tr : & BinderTransactionData ,
65
89
allow_fds : bool ,
66
- ) -> BinderResult < Self > {
67
- let alloc = from. copy_transaction_data ( & to, tr, allow_fds) ?;
90
+ ) -> BinderResult < Arc < Self > > {
91
+ let mut alloc = from. copy_transaction_data ( & to, tr, allow_fds) ?;
68
92
let data_address = alloc. ptr ;
93
+ let file_list = alloc. take_file_list ( ) ;
69
94
alloc. keep_alive ( ) ;
70
- Ok ( Self {
95
+ let mut tr = Arc :: try_new ( Self {
96
+ // SAFETY: `spinlock_init` is called below.
97
+ inner : unsafe { SpinLock :: new ( TransactionInner { file_list } ) } ,
71
98
node_ref : None ,
72
99
stack_next : None ,
73
100
from : from. clone ( ) ,
@@ -79,7 +106,13 @@ impl Transaction {
79
106
offsets_size : tr. offsets_size as _ ,
80
107
links : Links :: new ( ) ,
81
108
free_allocation : AtomicBool :: new ( true ) ,
82
- } )
109
+ } ) ?;
110
+
111
+ let mut_tr = Arc :: get_mut ( & mut tr) . ok_or ( Error :: EINVAL ) ?;
112
+
113
+ // SAFETY: `inner` is pinned behind `Arc`.
114
+ kernel:: spinlock_init!( Pin :: new_unchecked( & mut_tr. inner) , "Transaction::inner" ) ;
115
+ Ok ( tr)
83
116
}
84
117
85
118
/// Determines if the transaction is stacked on top of the given transaction.
@@ -136,6 +169,33 @@ impl Transaction {
136
169
process. push_work ( self )
137
170
}
138
171
}
172
+
173
+ /// Prepares the file list for delivery to the caller.
174
+ fn prepare_file_list ( & self ) -> Result < List < Box < FileInfo > > > {
175
+ // Get list of files that are being transferred as part of the transaction.
176
+ let mut file_list = core:: mem:: replace ( & mut self . inner . lock ( ) . file_list , List :: new ( ) ) ;
177
+
178
+ // If the list is non-empty, prepare the buffer.
179
+ if !file_list. is_empty ( ) {
180
+ let alloc = self . to . buffer_get ( self . data_address ) . ok_or ( Error :: ESRCH ) ?;
181
+ let cleanup = ScopeGuard :: new ( || {
182
+ self . free_allocation . store ( false , Ordering :: Relaxed ) ;
183
+ } ) ;
184
+
185
+ let mut it = file_list. cursor_front_mut ( ) ;
186
+ while let Some ( file_info) = it. current ( ) {
187
+ let reservation = FileDescriptorReservation :: new ( bindings:: O_CLOEXEC ) ?;
188
+ alloc. write ( file_info. buffer_offset , & reservation. reserved_fd ( ) ) ?;
189
+ file_info. reservation = Some ( reservation) ;
190
+ it. move_next ( ) ;
191
+ }
192
+
193
+ alloc. keep_alive ( ) ;
194
+ cleanup. dismiss ( ) ;
195
+ }
196
+
197
+ Ok ( file_list)
198
+ }
139
199
}
140
200
141
201
impl DeliverToRead for Transaction {
@@ -145,9 +205,19 @@ impl DeliverToRead for Transaction {
145
205
pub sender_euid: uid_t,
146
206
*/
147
207
let send_failed_reply = ScopeGuard :: new ( || {
148
- let reply = Either :: Right ( BR_FAILED_REPLY ) ;
149
- self . from . deliver_reply ( reply, & self ) ;
208
+ if self . node_ref . is_some ( ) && self . flags & TF_ONE_WAY == 0 {
209
+ let reply = Either :: Right ( BR_FAILED_REPLY ) ;
210
+ self . from . deliver_reply ( reply, & self ) ;
211
+ }
150
212
} ) ;
213
+ let mut file_list = if let Ok ( list) = self . prepare_file_list ( ) {
214
+ list
215
+ } else {
216
+ // On failure to process the list, we send a reply back to the sender and ignore the
217
+ // transaction on the recipient.
218
+ return Ok ( true ) ;
219
+ } ;
220
+
151
221
let mut tr = BinderTransactionData :: default ( ) ;
152
222
153
223
if let Some ( nref) = & self . node_ref {
@@ -165,10 +235,6 @@ impl DeliverToRead for Transaction {
165
235
tr. data . ptr . offsets = ( self . data_address + ptr_align ( self . data_size ) ) as _ ;
166
236
}
167
237
168
- // When `drop` is called, we don't want the allocation to be freed because it is now the
169
- // user's reponsibility to free it.
170
- self . free_allocation . store ( false , Ordering :: Relaxed ) ;
171
-
172
238
let code = if self . node_ref . is_none ( ) {
173
239
BR_REPLY
174
240
} else {
@@ -183,6 +249,27 @@ impl DeliverToRead for Transaction {
183
249
// here on out.
184
250
send_failed_reply. dismiss ( ) ;
185
251
252
+ // Commit all files.
253
+ {
254
+ let mut it = file_list. cursor_front_mut ( ) ;
255
+ while let Some ( file_info) = it. current ( ) {
256
+ if let Some ( reservation) = file_info. reservation . take ( ) {
257
+ if let Some ( file) = file_info. file . take ( ) {
258
+ reservation. commit ( file) ;
259
+ }
260
+ }
261
+
262
+ it. move_next ( ) ;
263
+ }
264
+ }
265
+
266
+ // When `drop` is called, we don't want the allocation to be freed because it is now the
267
+ // user's reponsibility to free it.
268
+ //
269
+ // `drop` is guaranteed to see this relaxed store because `Arc` guarantess that everything
270
+ // that happens when an object is referenced happens-before the eventual `drop`.
271
+ self . free_allocation . store ( false , Ordering :: Relaxed ) ;
272
+
186
273
// When this is not a reply and not an async transaction, update `current_transaction`. If
187
274
// it's a reply, `current_transaction` has already been updated appropriately.
188
275
if self . node_ref . is_some ( ) && tr. flags & TF_ONE_WAY == 0 {
@@ -209,3 +296,35 @@ impl Drop for Transaction {
209
296
}
210
297
}
211
298
}
299
+
300
+ pub ( crate ) struct FileInfo {
301
+ links : Links < FileInfo > ,
302
+
303
+ /// The file for which a descriptor will be created in the recipient process.
304
+ file : Option < File > ,
305
+
306
+ /// The file descriptor reservation on the recipient process.
307
+ reservation : Option < FileDescriptorReservation > ,
308
+
309
+ /// The offset in the buffer where the file descriptor is stored.
310
+ buffer_offset : usize ,
311
+ }
312
+
313
+ impl FileInfo {
314
+ pub ( crate ) fn new ( file : File , buffer_offset : usize ) -> Self {
315
+ Self {
316
+ file : Some ( file) ,
317
+ reservation : None ,
318
+ buffer_offset,
319
+ links : Links :: new ( ) ,
320
+ }
321
+ }
322
+ }
323
+
324
+ impl GetLinks for FileInfo {
325
+ type EntryType = Self ;
326
+
327
+ fn get_links ( data : & Self :: EntryType ) -> & Links < Self :: EntryType > {
328
+ & data. links
329
+ }
330
+ }
0 commit comments