@@ -434,6 +434,14 @@ struct FileDebugOutput {
434434#[ derive( Debug , Serialize ) ]
435435struct FileMetadata {
436436 size : u64 ,
437+ /// For virtual filesystems (procfs, sysfs), stat() returns 0 but reading
438+ /// the file may return actual content. This field stores the actual
439+ /// content size when available.
440+ #[ serde( skip_serializing_if = "Option::is_none" ) ]
441+ actual_size : Option < u64 > ,
442+ /// Whether the file is on a virtual filesystem (procfs, sysfs, etc.)
443+ #[ serde( skip_serializing_if = "Option::is_none" ) ]
444+ is_virtual_fs : Option < bool > ,
437445 is_file : bool ,
438446 is_dir : bool ,
439447 is_symlink : bool ,
@@ -495,9 +503,28 @@ async fn run_file(args: FileArgs) -> Result<()> {
495503 !is_writable_by_current_user ( & path)
496504 } ;
497505
506+ // Check if this is a virtual filesystem (procfs, sysfs, etc.)
507+ // These report size=0 in stat() but may have actual content
508+ let is_virtual_fs = is_virtual_filesystem ( & path) ;
509+ let stat_size = meta. len ( ) ;
510+
511+ // For virtual filesystem files that report 0 size, try to read actual content size
512+ let actual_size = if is_virtual_fs && stat_size == 0 && meta. is_file ( ) {
513+ // Try to read the file to get actual content size
514+ // Limit read to 1MB to avoid hanging on infinite streams
515+ match std:: fs:: read ( & path) {
516+ Ok ( content) if !content. is_empty ( ) => Some ( content. len ( ) as u64 ) ,
517+ _ => None ,
518+ }
519+ } else {
520+ None
521+ } ;
522+
498523 (
499524 Some ( FileMetadata {
500- size : meta. len ( ) ,
525+ size : stat_size,
526+ actual_size,
527+ is_virtual_fs : if is_virtual_fs { Some ( true ) } else { None } ,
501528 is_file : meta. is_file ( ) ,
502529 is_dir : meta. is_dir ( ) ,
503530 is_symlink : meta. file_type ( ) . is_symlink ( ) ,
@@ -577,7 +604,19 @@ async fn run_file(args: FileArgs) -> Result<()> {
577604 println ! ( ) ;
578605 println ! ( "Metadata" ) ;
579606 println ! ( "{}" , "-" . repeat( 40 ) ) ;
580- println ! ( " Size: {}" , format_size( meta. size) ) ;
607+ // Handle virtual filesystem files that report 0 size (#2829)
608+ if meta. is_virtual_fs . unwrap_or ( false ) {
609+ if let Some ( actual) = meta. actual_size {
610+ println ! (
611+ " Size: {} (stat reports 0, virtual filesystem)" ,
612+ format_size( actual)
613+ ) ;
614+ } else {
615+ println ! ( " Size: unknown (virtual filesystem)" ) ;
616+ }
617+ } else {
618+ println ! ( " Size: {}" , format_size( meta. size) ) ;
619+ }
581620
582621 // Display file type, including special types like FIFO, socket, etc.
583622 let type_str = if let Some ( ref special_type) = meta. file_type {
@@ -593,6 +632,9 @@ async fn run_file(args: FileArgs) -> Result<()> {
593632 } ;
594633 println ! ( " Type: {}" , type_str) ;
595634
635+ if meta. is_virtual_fs . unwrap_or ( false ) {
636+ println ! ( " Virtual: yes (procfs/sysfs/etc)" ) ;
637+ }
596638 println ! ( " Readonly: {}" , meta. readonly) ;
597639 if let Some ( ref modified) = meta. modified {
598640 println ! ( " Modified: {}" , modified) ;
@@ -733,6 +775,26 @@ fn detect_special_file_type(path: &std::path::Path) -> Option<String> {
733775 }
734776}
735777
778+ /// Check if the path is on a virtual filesystem like procfs or sysfs.
779+ /// These filesystems report size=0 in stat() for files that have actual content. (#2829)
780+ #[ cfg( target_os = "linux" ) ]
781+ fn is_virtual_filesystem ( path : & std:: path:: Path ) -> bool {
782+ let path_str = path. to_string_lossy ( ) ;
783+ path_str. starts_with ( "/proc/" )
784+ || path_str. starts_with ( "/sys/" )
785+ || path_str. starts_with ( "/dev/" )
786+ || path_str == "/proc"
787+ || path_str == "/sys"
788+ || path_str == "/dev"
789+ }
790+
791+ /// Check if the path is on a virtual filesystem like procfs or sysfs.
792+ /// On non-Linux systems, return false as these filesystems are Linux-specific.
793+ #[ cfg( not( target_os = "linux" ) ) ]
794+ fn is_virtual_filesystem ( _path : & std:: path:: Path ) -> bool {
795+ false
796+ }
797+
736798/// Detect encoding and binary status.
737799fn detect_encoding_and_binary ( path : & PathBuf ) -> ( Option < String > , Option < bool > ) {
738800 // Read first 8KB to check for binary content
0 commit comments