@@ -7,12 +7,14 @@ pub mod ram;
77use crate :: device:: Device ;
88use crate :: {
99 arch:: virt:: get_page_size,
10- memory:: { IovecIter , VirtAddr } ,
10+ memory:: { IovecIter , VirtAddr , user :: UserPtr } ,
1111 posix:: errno:: { EResult , Errno } ,
1212 process:: Identity ,
13+ uapi,
14+ util:: mutex:: spin:: SpinMutex ,
1315 vfs:: { self , File , file:: FileOps , fs:: devtmpfs, inode:: Mode } ,
1416} ;
15- use alloc:: { format, sync:: Arc , vec:: Vec } ;
17+ use alloc:: { collections :: btree_map :: BTreeMap , format, string :: String , sync:: Arc , vec:: Vec } ;
1618
1719pub use bio:: { BioRequest , BlockLimits } ;
1820pub use io:: { BlockBuffer , BlockOp , BlockSegment } ;
@@ -256,6 +258,14 @@ pub fn BLOCK_STAGE() {
256258 . expect ( "Unable to create /dev/block" ) ;
257259}
258260
261+ struct RegisteredDisk {
262+ device : Arc < dyn BlockDevice > ,
263+ partition_nodes : Vec < String > ,
264+ }
265+
266+ static REGISTERED_DISKS : SpinMutex < BTreeMap < String , RegisteredDisk > > =
267+ SpinMutex :: new ( BTreeMap :: new ( ) ) ;
268+
259269/// Registers a block device by name and scans for partitions.
260270pub fn register_block_device ( name : & str , device : Arc < dyn BlockDevice > ) -> EResult < ( ) > {
261271 // Register in devtmpfs as well.
@@ -267,19 +277,63 @@ pub fn register_block_device(name: &str, device: Arc<dyn BlockDevice>) -> EResul
267277
268278 log ! ( "Registered block device: \" {}\" " , name) ;
269279
280+ REGISTERED_DISKS . lock ( ) . insert (
281+ String :: from ( name) ,
282+ RegisteredDisk {
283+ device : device. clone ( ) ,
284+ partition_nodes : Vec :: new ( ) ,
285+ } ,
286+ ) ;
287+
270288 // Scan for GPT partitions.
271289 scan_partitions ( name, device) ?;
272290
273291 Ok ( ( ) )
274292}
275293
294+ /// Looks up the registered name of a whole-disk block device.
295+ fn registered_disk_name ( device : & dyn BlockDevice ) -> Option < String > {
296+ let target = device as * const dyn BlockDevice as * const ( ) ;
297+ REGISTERED_DISKS
298+ . lock ( )
299+ . iter ( )
300+ . find ( |( _, disk) | Arc :: as_ptr ( & disk. device ) as * const ( ) == target)
301+ . map ( |( name, _) | name. clone ( ) )
302+ }
303+
304+ /// Drops every partition node of a disk and scans its partition table again.
305+ pub fn rescan_partitions ( name : & str ) -> EResult < ( ) > {
306+ let ( device, stale) = {
307+ let mut disks = REGISTERED_DISKS . lock ( ) ;
308+ let disk = disks. get_mut ( name) . ok_or ( Errno :: ENODEV ) ?;
309+ (
310+ disk. device . clone ( ) ,
311+ core:: mem:: take ( & mut disk. partition_nodes ) ,
312+ )
313+ } ;
314+
315+ for node in stale {
316+ if let Err ( e) = crate :: device:: unregister_node ( node. as_bytes ( ) ) {
317+ log ! (
318+ "Unable to remove stale partition node \" {}\" : {:?}" ,
319+ node,
320+ e
321+ ) ;
322+ }
323+ }
324+
325+ scan_partitions ( name, device)
326+ }
327+
276328/// Scans a block device for GPT partitions and registers each as a sub-device.
277329fn scan_partitions ( parent_name : & str , device : Arc < dyn BlockDevice > ) -> EResult < ( ) > {
278330 let partitions = match gpt:: scan_gpt ( device. clone ( ) ) {
279331 Ok ( p) => p,
280332 Err ( _) => return Ok ( ( ) ) , // No GPT found, that's fine.
281333 } ;
282334
335+ let mut nodes = Vec :: new ( ) ;
336+
283337 for ( i, part) in partitions. iter ( ) . enumerate ( ) {
284338 let part_name = format ! ( "{}p{}" , parent_name, i + 1 ) ;
285339 let part_dev = Arc :: new ( partition:: PartitionDevice :: new (
@@ -288,31 +342,45 @@ fn scan_partitions(parent_name: &str, device: Arc<dyn BlockDevice>) -> EResult<(
288342 part. end_lba - part. start_lba + 1 ,
289343 ) ) ;
290344
345+ let node = format ! ( "block/{}" , part_name) ;
291346 crate :: device:: register_block_node (
292- format ! ( "block/{}" , part_name ) . as_bytes ( ) ,
347+ node . as_bytes ( ) ,
293348 part_dev,
294349 Mode :: from_bits_truncate ( 0o660 ) ,
295350 ) ?;
351+ nodes. push ( node) ;
296352
297353 let root = devtmpfs:: get_root ( ) ;
298354 let uuid_str = part. unique_guid . to_string ( ) ;
299355 let type_str = part. type_guid . to_string ( ) ;
300356
301- // TODO: This could conflict with other partitions.
302- vfs:: symlink (
357+ let type_link = format ! ( "block/parttype-{}" , type_str ) ;
358+ match vfs:: symlink (
303359 root. clone ( ) ,
304360 root. clone ( ) ,
305- format ! ( "block/parttype-{}" , type_str ) . as_bytes ( ) ,
361+ type_link . as_bytes ( ) ,
306362 part_name. as_bytes ( ) ,
307363 & Identity :: get_kernel ( ) ,
308- ) ?;
364+ ) {
365+ Ok ( _) => nodes. push ( type_link) ,
366+ Err ( Errno :: EEXIST ) => log ! (
367+ "Partition type {} already claimed, no \" {}\" link for \" {}\" " ,
368+ type_str,
369+ type_link,
370+ part_name
371+ ) ,
372+ Err ( e) => return Err ( e) ,
373+ }
374+
375+ let uuid_link = format ! ( "block/partuuid-{}" , uuid_str) ;
309376 vfs:: symlink (
310377 root. clone ( ) ,
311378 root. clone ( ) ,
312- format ! ( "block/partuuid-{}" , uuid_str ) . as_bytes ( ) ,
379+ uuid_link . as_bytes ( ) ,
313380 part_name. as_bytes ( ) ,
314381 & Identity :: get_kernel ( ) ,
315382 ) ?;
383+ nodes. push ( uuid_link) ;
316384
317385 log ! (
318386 "Partition {}: \" {}\" Type: {} UUID: {}" ,
@@ -323,6 +391,10 @@ fn scan_partitions(parent_name: &str, device: Arc<dyn BlockDevice>) -> EResult<(
323391 ) ;
324392 }
325393
394+ if let Some ( disk) = REGISTERED_DISKS . lock ( ) . get_mut ( parent_name) {
395+ disk. partition_nodes = nodes;
396+ }
397+
326398 Ok ( ( ) )
327399}
328400
@@ -447,6 +519,23 @@ impl<T: BlockDevice> FileOps for T {
447519 }
448520
449521 fn ioctl ( & self , file : & File , request : usize , arg : VirtAddr ) -> EResult < usize > {
450- self . handle_ioctl ( file, request, arg)
522+ match request as u32 {
523+ uapi:: ioctls:: BLKGETSIZE64 => {
524+ let size = self . lba_count ( ) * self . get_lba_size ( ) as u64 ;
525+ UserPtr :: new ( arg) . write ( size) . ok_or ( Errno :: EFAULT ) ?;
526+ Ok ( 0 )
527+ }
528+ uapi:: ioctls:: BLKSSZGET => {
529+ let lba_size = self . get_lba_size ( ) as u32 ;
530+ UserPtr :: new ( arg) . write ( lba_size) . ok_or ( Errno :: EFAULT ) ?;
531+ Ok ( 0 )
532+ }
533+ uapi:: ioctls:: BLKRRPART => {
534+ let name = registered_disk_name ( self ) . ok_or ( Errno :: ENOTTY ) ?;
535+ rescan_partitions ( & name) ?;
536+ Ok ( 0 )
537+ }
538+ _ => self . handle_ioctl ( file, request, arg) ,
539+ }
451540 }
452541}
0 commit comments