diff --git a/src/service.rs b/src/service.rs index e6eac1d..203710b 100644 --- a/src/service.rs +++ b/src/service.rs @@ -44,13 +44,33 @@ impl IntoTonicResult for OperationResult { } } +fn major_minor_from_stat(stat: rustix::fs::Stat) -> (u32, u32) { + ( + rustix::fs::major(stat.st_rdev), + rustix::fs::minor(stat.st_rdev), + ) +} + fn devnode_major_minor(path: &str) -> tonic::Result<(u32, u32)> { let stat = rustix::fs::stat(path) .map_err(|e| tonic::Status::internal(format!("stat {path}: {e:?}")))?; - Ok(( - rustix::fs::major(stat.st_rdev), - rustix::fs::minor(stat.st_rdev), - )) + Ok(major_minor_from_stat(stat)) +} + +async fn devnode_major_minor_retry_missing(path: &str) -> tonic::Result<(u32, u32)> { + for _ in 0..50 { + match rustix::fs::stat(path) { + Ok(stat) => return Ok(major_minor_from_stat(stat)), + Err(e) if e.kind() == std::io::ErrorKind::NotFound => { + tokio::time::sleep(Duration::from_millis(100)).await; + } + Err(e) => return Err(tonic::Status::internal(format!("stat {path}: {e:?}"))), + } + } + + Err(tonic::Status::deadline_exceeded(format!( + "waiting for devnode {path}" + ))) } fn get_size_bytes_unsigned(size_bytes: i64) -> tonic::Result { @@ -415,7 +435,7 @@ impl lvmd::lv_service_server::LvService for Service { info!("retrieving devnode information"); let devnode = fs.proxy.devnode().await.into_tonic_result()?; - let (dev_major, dev_minor) = devnode_major_minor(&devnode)?; + let (dev_major, dev_minor) = devnode_major_minor_retry_missing(&devnode).await?; #[allow(deprecated)] Ok(tonic::Response::new(lvmd::CreateLvResponse { @@ -595,7 +615,7 @@ impl lvmd::lv_service_server::LvService for Service { info!("retrieving devnode information"); let devnode = snapshot.proxy.devnode().await.into_tonic_result()?; - let (dev_major, dev_minor) = devnode_major_minor(&devnode)?; + let (dev_major, dev_minor) = devnode_major_minor_retry_missing(&devnode).await?; #[allow(deprecated)] Ok(tonic::Response::new(lvmd::CreateLvSnapshotResponse {