diff --git a/lib/Overload/FileCheck.pm b/lib/Overload/FileCheck.pm index 5034976..c368c8f 100644 --- a/lib/Overload/FileCheck.pm +++ b/lib/Overload/FileCheck.pm @@ -628,43 +628,6 @@ sub _stat_for { my @stat = ( (0) x STAT_T_MAX ); - # set file type - if ( defined $type ) { - - # _S_IFMT is used as a protection to do not flip outside the mask - $stat[ST_MODE] |= ( $type & _S_IFMT ); - } - - # set permission using octal - if ( defined $opts->{perms} ) { - - # _S_IFMT is used as a protection to do not flip outside the mask - $stat[ST_MODE] |= ( $opts->{perms} & ~_S_IFMT ); - } - - # deal with UID / GID - if ( defined $opts->{uid} ) { - if ( $opts->{uid} =~ qr{^[0-9]+$} ) { - $stat[ST_UID] = $opts->{uid}; - } - else { - my $uid = getpwnam( $opts->{uid} ); - Carp::croak("Unknown user '$opts->{uid}' passed to uid option") unless defined $uid; - $stat[ST_UID] = $uid; - } - } - - if ( defined $opts->{gid} ) { - if ( $opts->{gid} =~ qr{^[0-9]+$} ) { - $stat[ST_GID] = $opts->{gid}; - } - else { - my $gid = getgrnam( $opts->{gid} ); - Carp::croak("Unknown group '$opts->{gid}' passed to gid option") unless defined $gid; - $stat[ST_GID] = $gid; - } - } - # options that we can simply copy to a slot my %name2ix = ( dev => ST_DEV, @@ -682,6 +645,9 @@ sub _stat_for { # all valid option names (after normalization: lc + strip st_ prefix) my %known_opts = ( perms => 1, uid => 1, gid => 1, map { $_ => 1 } keys %name2ix ); + # Normalize all option keys upfront (lc + strip st_ prefix) so that + # uid/gid/perms handlers work with all key variants (e.g. st_uid, ST_GID). + my %norm; foreach my $orig_key ( keys %$opts ) { my $k = lc($orig_key); $k =~ s{^st_}{}; @@ -693,8 +659,50 @@ sub _stat_for { Carp::croak("Unknown option '$orig_key' passed to stat helper"); } + $norm{$k} = $opts->{$orig_key}; + } + + # set file type + if ( defined $type ) { + + # _S_IFMT is used as a protection to do not flip outside the mask + $stat[ST_MODE] |= ( $type & _S_IFMT ); + } + + # set permission using octal + if ( defined $norm{perms} ) { + + # _S_IFMT is used as a protection to do not flip outside the mask + $stat[ST_MODE] |= ( $norm{perms} & ~_S_IFMT ); + } + + # deal with UID / GID + if ( defined $norm{uid} ) { + if ( $norm{uid} =~ qr{^[0-9]+$} ) { + $stat[ST_UID] = $norm{uid}; + } + else { + my $uid = getpwnam( $norm{uid} ); + Carp::croak("Unknown user '$norm{uid}' passed to uid option") unless defined $uid; + $stat[ST_UID] = $uid; + } + } + + if ( defined $norm{gid} ) { + if ( $norm{gid} =~ qr{^[0-9]+$} ) { + $stat[ST_GID] = $norm{gid}; + } + else { + my $gid = getgrnam( $norm{gid} ); + Carp::croak("Unknown group '$norm{gid}' passed to gid option") unless defined $gid; + $stat[ST_GID] = $gid; + } + } + + # copy remaining simple slots + foreach my $k ( keys %norm ) { next unless defined $name2ix{$k}; - $stat[ $name2ix{$k} ] = $opts->{$orig_key}; + $stat[ $name2ix{$k} ] = $norm{$k}; } return \@stat; diff --git a/t/stat-key-normalization.t b/t/stat-key-normalization.t index 0b2104e..9e5995c 100644 --- a/t/stat-key-normalization.t +++ b/t/stat-key-normalization.t @@ -50,4 +50,37 @@ my @base = ( 0, 0, S_IFREG, (0) x 10 ); is stat_as_file( Mtime => 12345 ), $expect, 'mixed-case Mtime key'; } +# uid/gid with st_ prefix — should resolve through getpwnam/getgrnam path +# (or set numeric value directly) +{ + my $expect = [@base]; + $expect->[4] = 42; + is stat_as_file( st_uid => 42 ), $expect, 'st_uid prefix sets uid slot'; +} + +{ + my $expect = [@base]; + $expect->[4] = 99; + is stat_as_file( ST_UID => 99 ), $expect, 'ST_UID uppercase prefix sets uid slot'; +} + +{ + my $expect = [@base]; + $expect->[5] = 7; + is stat_as_file( st_gid => 7 ), $expect, 'st_gid prefix sets gid slot'; +} + +{ + my $expect = [@base]; + $expect->[5] = 100; + is stat_as_file( ST_GID => 100 ), $expect, 'ST_GID uppercase prefix sets gid slot'; +} + +# perms with mixed case +{ + my $expect = [@base]; + $expect->[2] = S_IFREG | 0755; + is stat_as_file( Perms => 0755 ), $expect, 'Perms mixed-case sets mode bits'; +} + done_testing;