diff --git a/.github/workflows/cygwin.yml b/.github/workflows/cygwin.yml deleted file mode 100644 index 3a152fd..0000000 --- a/.github/workflows/cygwin.yml +++ /dev/null @@ -1,92 +0,0 @@ -name: cygwin - -on: - push: - branches: - - '*' - tags-ignore: - - '*' - pull_request: - -env: - PERL5LIB: /cygdrive/c/cx/lib/perl5 - PERL_LOCAL_LIB_ROOT: /cygdrive/cx - PERL_MB_OPT: --install_base /cygdrive/c/cx - PERL_MM_OPT: INSTALL_BASE=/cygdrive/c/cx - ALIEN_BUILD_PLUGIN_PKGCONFIG_COMMANDLINE_TEST: 1 # Test Alien::Build::Plugin::PkgConfig::CommandLine - CYGWIN_NOWINPATH: 1 - -jobs: - perl: - - runs-on: windows-latest - - strategy: - fail-fast: false - - defaults: - run: - shell: C:\tools\cygwin\bin\bash.exe --login --norc -eo pipefail -o igncr '{0}' - - steps: - - name: Set git to use LF - run: | - git config --global core.autocrlf false - git config --global core.eol lf - shell: powershell - - - uses: actions/checkout@v2 - - - name: Set up Cygwin - uses: egor-tensin/setup-cygwin@v3 - with: - platform: x64 - packages: make perl gcc-core gcc-g++ pkg-config libcrypt-devel libssl-devel git libffi-devel libarchive-devel - - - name: perl -V - run: | - perl -V - gcc --version - - - name: Prepare for cache - run: | - perl -V > perlversion.txt - gcc --version >> perlversion.txt - ls perlversion.txt - - - name: Cache CPAN modules - uses: actions/cache@v1 - with: - path: c:\cx - key: ${{ runner.os }}-build-cygwin-${{ hashFiles('perlversion.txt') }} - restore-keys: | - ${{ runner.os }}-build-cygwin-${{ hashFiles('perlversion.txt') }} - - - name: Install Static Dependencies - run: | - export PATH="/cygdrive/c/cx/bin:$PATH" - cd $( cygpath -u $GITHUB_WORKSPACE ) - yes | cpan App::cpanminus || true - cpanm -n Dist::Zilla - perl -S dzil authordeps --missing | perl -S cpanm -n - perl -S dzil listdeps --missing | perl -S cpanm -n - - - name: Install Dynamic Dependencies - run: | - export PATH="/cygdrive/c/cx/bin:$PATH" - cd $( cygpath -u $GITHUB_WORKSPACE ) - perl -S dzil run --no-build 'perl -S cpanm --installdeps .' - - - name: Run Tests - run: | - export PATH="/cygdrive/c/cx/bin:$PATH" - cd $( cygpath -u $GITHUB_WORKSPACE ) - perl -S dzil test -v - - - name: CPAN log - if: ${{ failure() }} - run: | - cat ~/.cpanm/latest-build/build.log - - - diff --git a/Changes b/Changes index efc19f3..aec1c7a 100644 --- a/Changes +++ b/Changes @@ -1,6 +1,12 @@ Revision history for {{$dist->name}} {{$NEXT}} + - Passing no arguments to new will now return an + uninitialized stat object (gh#9, gh#10) + - Passing undef to clone will now return a stat + object with the memory set to all zeros, + previously the behavior was undefined and could + crash (gh#10) 0.02 2021-05-31 05:48:05 -0600 - Work around possible bug in Perl stat function diff --git a/README.md b/README.md index 4ae989c..5263a4b 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# FFI::C::Stat ![static](https://github.com/PerlFFI/FFI-C-Stat/workflows/static/badge.svg) ![linux](https://github.com/PerlFFI/FFI-C-Stat/workflows/linux/badge.svg) ![windows](https://github.com/PerlFFI/FFI-C-Stat/workflows/windows/badge.svg) ![macos](https://github.com/PerlFFI/FFI-C-Stat/workflows/macos/badge.svg) ![cygwin](https://github.com/PerlFFI/FFI-C-Stat/workflows/cygwin/badge.svg) ![msys2-mingw](https://github.com/PerlFFI/FFI-C-Stat/workflows/msys2-mingw/badge.svg) +# FFI::C::Stat ![static](https://github.com/PerlFFI/FFI-C-Stat/workflows/static/badge.svg) ![linux](https://github.com/PerlFFI/FFI-C-Stat/workflows/linux/badge.svg) ![windows](https://github.com/PerlFFI/FFI-C-Stat/workflows/windows/badge.svg) ![macos](https://github.com/PerlFFI/FFI-C-Stat/workflows/macos/badge.svg) ![msys2-mingw](https://github.com/PerlFFI/FFI-C-Stat/workflows/msys2-mingw/badge.svg) Object-oriented FFI interface to native stat and lstat @@ -46,10 +46,12 @@ $ffi->attach( my_cfunction => ['stat'] => 'void' ); ```perl my $stat = FFI::C::Stat->new(*HANDLE, %options); my $stat = FFI::C::Stat->new($filename, %options); +my $stat = FFI::C::Stat->new; ``` You can create a new instance of this class by calling the new method and passing in -either a file or directory handle, or by passing in the filename path. +either a file or directory handle, or by passing in the filename path. If you do +not pass anything then an uninitialized stat will be returned. Options: @@ -91,6 +93,10 @@ $ffi->attach( my_cfunction => [] => 'opaque' => sub { }); ``` +The behavior of passing in `undef` prior to version 0.03 was undefined and could cause a +crash. In version 0.03 and later passing in `undef` will return a stat object with all +of the bits set to zero (0). + # PROPERTIES ## dev @@ -203,7 +209,7 @@ Graham Ollis # COPYRIGHT AND LICENSE -This software is copyright (c) 2021 by Graham Ollis. +This software is copyright (c) 2021-2023 by Graham Ollis. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. diff --git a/dist.ini b/dist.ini index ffd3ba3..20bc9d0 100644 --- a/dist.ini +++ b/dist.ini @@ -2,7 +2,7 @@ name = FFI-C-Stat author = Graham Ollis license = Perl_5 copyright_holder = Graham Ollis -copyright_year = 2021-2022 +copyright_year = 2021-2023 version = 0.02 [@Author::Plicease] @@ -18,7 +18,6 @@ workflow = static workflow = linux workflow = windows workflow = macos -workflow = cygwin workflow = msys2-mingw [Author::Plicease::Core] diff --git a/ffi/stat.c b/ffi/stat.c index cd312fc..90e5614 100644 --- a/ffi/stat.c +++ b/ffi/stat.c @@ -54,12 +54,27 @@ stat___fstat(int fd) } struct stat * -stat__clone(struct stat *self) +stat___new(void) { - struct stat *other; - other = malloc(sizeof(struct stat)); - memcpy(other, self, sizeof(struct stat)); - return other; + struct stat *self; + self = malloc(sizeof(struct stat)); + return self; +} + +struct stat * +stat__clone(struct stat *other) +{ + struct stat *self; + self = malloc(sizeof(struct stat)); + if(other == NULL) + { + memset(self, 0, sizeof(struct stat)); + } + else + { + memcpy(self, other, sizeof(struct stat)); + } + return self; } dev_t diff --git a/lib/FFI/C/Stat.pm b/lib/FFI/C/Stat.pm index 72072bc..b43c9cf 100644 --- a/lib/FFI/C/Stat.pm +++ b/lib/FFI/C/Stat.pm @@ -54,6 +54,7 @@ $ffi->mangler(sub { "stat__$_[0]" }); $ffi->attach( '_stat' => ['string'] => 'opaque' ); $ffi->attach( '_fstat' => ['int', ] => 'opaque' ); $ffi->attach( '_lstat' => ['string'] => 'opaque' ); +$ffi->attach( '_new' => [] => 'opaque' ); =head1 CONSTRUCTORS @@ -61,9 +62,11 @@ $ffi->attach( '_lstat' => ['string'] => 'opaque' ); my $stat = FFI::C::Stat->new(*HANDLE, %options); my $stat = FFI::C::Stat->new($filename, %options); + my $stat = FFI::C::Stat->new; You can create a new instance of this class by calling the new method and passing in -either a file or directory handle, or by passing in the filename path. +either a file or directory handle, or by passing in the filename path. If you do +not pass anything then an uninitialized stat will be returned. Options: @@ -87,6 +90,8 @@ sub new { $ptr = _fstat(fileno($file)) } elsif(!is_ref($file) && defined $file) { $ptr = $options{symlink} ? _lstat($file) : _stat($file) } + elsif(!defined $file) + { $ptr = _new() } else { Carp::croak("Tried to stat something whch is neither a glob reference nor a plain string") } @@ -120,6 +125,10 @@ Perl: return FFI::C::Stat->clone($ptr); }); +The behavior of passing in C prior to version 0.03 was undefined and could cause a +crash. In version 0.03 and later passing in C will return a stat object with all +of the bits set to zero (0). + =cut $ffi->attach( clone => ['opaque'] => 'opaque' => sub { diff --git a/t/ffi_c_stat.t b/t/ffi_c_stat.t index e0d2b6a..f4a182a 100644 --- a/t/ffi_c_stat.t +++ b/t/ffi_c_stat.t @@ -71,6 +71,15 @@ is( 'clone a stat', ); +is( + FFI::C::Stat->clone(undef), + object { + call [ isa => 'FFI::C::Stat' ] => T(); + call $_ => D() for @props; + }, + 'clone undef', +); + { my $other = FFI::C::Stat->new('corpus/xx.txt'); is( @@ -122,4 +131,13 @@ if($Config{d_symlink} eq 'define') unlink 'testlink'; +is( + FFI::C::Stat->new, + object { + call [ isa => 'FFI::C::Stat' ] => T(); + call $_ => D() for @props; + }, + 'create uninitalized stat', +); + done_testing;