1717package com .cloud .hypervisor .kvm .storage ;
1818
1919import java .io .File ;
20+ import java .io .FileWriter ;
21+ import java .nio .file .Path ;
2022import java .util .HashMap ;
2123import java .util .List ;
2224import java .util .Map ;
@@ -363,6 +365,15 @@ private boolean hasOtherActiveLuns(String host, int port, String iqn, String lun
363365 // independent LUNs, they are partition symlinks for the same LUN disk.
364366 // Only count actual LUN entries (no "-part" suffix after the lun number).
365367 if (name .startsWith (prefix ) && !name .equals (prefix + lun ) && !name .contains ("-part" )) {
368+ // Skip dangling symlinks — after removeStaleScsiDevice() deletes the sysfs
369+ // device, the by-path symlink becomes dangling before udev removes it.
370+ // Counting a dangling symlink as an "active" LUN would cause the second
371+ // of two sequential disconnects to incorrectly skip logout.
372+ // Files.exists() follows symlinks: returns false if the target block device is gone.
373+ if (!Files .exists (entry .toPath ())) {
374+ logger .debug ("Skipping dangling by-path symlink (device already removed): " + name );
375+ continue ;
376+ }
366377 logger .debug ("Found other active LUN on same target: " + name );
367378 return true ;
368379 }
@@ -387,20 +398,20 @@ private boolean hasOtherActiveLuns(String host, int port, String iqn, String lun
387398 */
388399 private void removeStaleScsiDevice (String host , int port , String iqn , String lun ) {
389400 String byPath = getByPath (host , port , "/" + iqn + "/" + lun );
390- java . nio . file . Path byPathLink = java . nio . file . Paths .get (byPath );
391- if (!java . nio . file . Files .exists (byPathLink )) {
401+ Path byPathLink = Paths .get (byPath );
402+ if (!Files .exists (byPathLink )) {
392403 logger .debug ("by-path entry for LUN " + lun + " already gone, nothing to remove" );
393404 return ;
394405 }
395406 try {
396- java . nio . file . Path realDevice = byPathLink .toRealPath ();
407+ Path realDevice = byPathLink .toRealPath ();
397408 String devName = realDevice .getFileName ().toString ();
398- java . io . File deleteFile = new java . io . File ("/sys/block/" + devName + "/device/delete" );
409+ File deleteFile = new File ("/sys/block/" + devName + "/device/delete" );
399410 if (!deleteFile .exists ()) {
400411 logger .warn ("sysfs delete entry not found for device " + devName + " — cannot remove stale SCSI device" );
401412 return ;
402413 }
403- try (java . io . FileWriter fw = new java . io . FileWriter (deleteFile )) {
414+ try (FileWriter fw = new FileWriter (deleteFile )) {
404415 fw .write ("1" );
405416 }
406417 logger .info ("Removed stale SCSI device " + devName + " for LUN /" + iqn + "/" + lun + " via sysfs" );
@@ -414,6 +425,7 @@ private boolean disconnectPhysicalDisk(String host, int port, String iqn, String
414425 // ONTAP (and similar) uses a single IQN per SVM with multiple LUNs.
415426 // Doing iscsiadm --logout tears down the ENTIRE target session,
416427 // which would destroy access to ALL LUNs — not just the one being disconnected.
428+ //
417429 if (hasOtherActiveLuns (host , port , iqn , lun )) {
418430 logger .info ("Skipping iSCSI logout for /" + iqn + "/" + lun +
419431 " — other LUNs on the same target are still active. Removing stale SCSI device for this LUN only." );
0 commit comments