8000 Content of junction directories gets deleted after running composer update · Issue #4955 · composer/composer · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Content of junction directories gets deleted after running composer update #4955

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
hediet opened this issue Feb 24, 2016 · 11 comments
Closed
Labels
Milestone

Comments

@hediet
Copy link
hediet commented Feb 24, 2016

In my scenario there are two composer projects: "lib" and "main". "main" has a dependency to "lib".
As I am developing both projects simultaneously I used the path type repository feature that instructs composer to create a junction directory rather than downloading the "lib" project from packagist.org.
However, when I now remove the dependency from "main" to "lib" and run composer update in the "main" directory, composer deletes the whole content of the "lib" directory instead of just removing the junction directory. This almost led to a devastating loss of really much work...

Steps to reproduce

This is the composer.json file of the lib project in the directory "lib":

{
    "name": "hediet/lib"
}

And of the main project in the directory "main":

{
    "name": "hediet/main",
    "require": {
        "hediet/lib": "*"
    },
    "repositories": [
        {
            "type": "path",
            "url": "C:\\Users\\Henning\\Desktop\\composer-bug\\lib"
        }
    ]
}

After running composer update in both the lib and the main directory, I removed the dependency from main to lib. Then I ran composer update again.

Expected result

The vendor directory in the main directory is empty, the original lib directory including any files in it are not touched by the composer updates.

Actual result

The vendor directory in the main directory is empty and the lib directory on root level is now empty too. All files (and subdirectories) of the lib directory were deleted with the last composer update.

@hediet hediet changed the title Content of junctioned repositories gets deleted Content of junction directories gets deleted after running composer update Feb 24, 2016
@curry684
Copy link
Contributor

Please know I take this issue very seriously as it's my code and I feared it may have such consequences, as PHP does not provide any kind of API to reliably detect junctions itself. We did a few doublechecks before releasing, ie. #4690 (comment). I will try to reproduce and fix your issue when I have time. I may need a Skype or IRC chat to reproduce your case if you don't mind that.

@curry684
Copy link
Contributor

Thinking some more and I suspect this is a nasty fringe case where a recursive delete is still used when purging packages, while updates and regular removes detect the junction correctly. As the Symfony filesystem class is oblivious of junctions, and NTFS junctions are close to undetectable anyway, a recursive delete does not notice the junction at all and just continues deleting files inside the linked folder. Thankfully this means it shouldn't be too hard to fix.

@alcohol alcohol added the Bug label Feb 25, 2016
@Seldaek Seldaek added this to the Bugs milestone Feb 25, 2016
@Seldaek
Copy link
Member
Seldaek commented Feb 25, 2016

@curry684 seeing as we only use junctions in PathDownloader, it might be worth implementing PathDownloader::remove() and just doing a removeJunction always in there if we're on windows. I mean always attempt removing the junction using the proper command, and if that fails then run a regular remove?

However from what I can tell in Util\Filesystem, removeJunction basically just runs the same command as remove. So the only case I can see where it'd fail is if proc_open() isn't defined, but if that was the case the junction couldn't have been created either.

It's all quite odd.

@curry684
Copy link
Contributor

Just tried to reproduce this and no luck at all - several install/uninstall cycles each correctly detected and removed the junction. We implemented it at the right level, as FileDownloader::remove itself just calls Filesystem::removeDirectory which detects junctions correctly afaics. So implementing PathDownloader::remove should be obsolete. Using it could be a valid workaround on Windows if we can't discover the real cause indeed, as the rmdir approach will clean up nicely anyway (Windows rmdir acts as rm on *nix with symlinks, not resolving recursively).

So the only thing that can go wrong is either proc_open failing as you suggest, or isJunction failing to report properly and thus falling back to recursive delete. I'm suspecting the second as it's the dodgiest part, given the lack of any proper official detection method.

@hediet which exact Windows version are you using? And can you run the following PHP snippet on the vendor/hediet/main folder in your including package when the package is loaded:

    $junction = '/path/to/vendor/hediet/main';
    if (!is_dir($junction) || is_link($junction)) {
        die 'Invalid premises';
    }
    print_r(lstat($junction));

I'm very interested in the output.

@hediet
Copy link
Author
hediet commented Feb 25, 2016

I totally forget to include my system configuration, sorry for that:

php -r "echo php_uname();"
>> Windows NT HENNING-D 10.0 build 10586 (Windows 10) i586
(it is Windows 10 Education 64 Bit)

php --version
>> PHP 7.0.3 (cli) (built: Feb  2 2016 14:41:10) ( NTS )
Copyright (c) 1997-2016 The PHP Group
Zend Engine v3.0.0, Copyright (c) 1998-2016 Zend Technologies
(I am using the 32 bit version)

composer --version
>> Composer version 1.0-dev (00d12526ec966cc75817c0bf43dd55c76dcbf5c0) 2016-02-24 18:24:40

php test.php (after enclosing the argument of "die" with parenthesis)
>> Array
(
    [0] => 11
    [1] => 0
    [2] => 26854
    [3] => 1
    [4] => 0
    [5] => 0
    [6] => 11
    [7] => 0
    [8] => 1456412299
    [9] => 1456412299
    [10] => 1456412299
    [11] => -1
    [12] => -1
    [dev] => 11
    [ino] => 0
    [mode] => 26854
    [nlink] => 1
    [uid] => 0
    [gid] => 0
    [rdev] => 11
    [size] => 0
    [atime] => 1456412299
    [mtime] => 1456412299
    [ctime] => 1456412299
    [blksize] => -1
    [blocks] => -1
)

It is new to me that die 'text'; is invalid in php 7.
Besides, for any reason, my steps to reproduce don't work any more.
I had to change "hediet/lib": "*" to "hediet/lib": "*@dev", otherwise composer complained that it couldn't find the package "hediet/lib" (why is that?).

I noticed that the actual behavior is slightly different from what I described earlier:
The directory "lib" itself now gets deleted too. (Yesterday, I had an active console prompt in the "lib" directory that prevented anyone from deleting the directory - however, composer didn't complain about that and just ignored it)
Now I got this:

C:\Users\henni\Desktop\php-bug\main>composer update
Loading composer repositories with package information
Updating dependencies (including require-dev)
  - Installing hediet/lib (dev-master)
    Junctioned from C:/Users/henni/Desktop/php-bug/lib/

Writing lock file
Generating autoload files

// After removing the dependency from hediet/main to hediet/lib:

C:\Users\henni\Desktop\php-bug\main>composer update
Loading composer repositories with package information
Updating dependencies (including require-dev)
  - Removing hediet/lib (dev-master)


  [UnexpectedValueException]
  RecursiveDirectoryIterator::__construct(C:\Users\henni\Desktop\php-bug\main
  \vendor/hediet/lib,C:\Users\henni\Desktop\php-bug\main\vendor/hediet/lib):
  Das System kann den angegebenen Pfad nicht finden. (code: 3)


update [--prefer-source] [--prefer-dist] [--dry-run] [--dev] [--no-dev] [--lock] [--no-plugins] [--no-custom-installers] [--no-autoloader] [--no-scripts] [--no-progress] [--with-dependencies] [-v|vv|vvv|--verbose] [-o|--optimize-autoloader] [-a|--classmap-authoritative] [--ignore-platform-reqs] [--prefer-stable] [--prefer-lowest] [-i|--interactive] [--] [<packages>]...

And the lib directory now is gone too.
You can contact my via henning.dieterichs on Skype if you want to try out something interactively.

@stof
Copy link
Contributor
stof commented Feb 25, 2016

esterday, I had an active console prompt in the "lib" directory that prevented anyone from deleting the directory - however, composer didn't complain about that and just ignored it

the fact that removing it failed might be why the content was deleted though. It may totally be related.

@curry684
Copy link
Contributor

[mode] => 26854

That is really weird, this seems to indicate PHP is thinking it's a true folder.

the fact that removing it failed might be why the content was deleted though. It may totally be related.

It could most certainly cause scary behavior as a lot of the (un)install logic in Composer core falls back to recursive delete if cleaner methods fail. So yes, this would most certainly explain some weirdo behaviors. I'll look into shortwiring that completely to Windows-native commands to prevent all of that from ever possibly happening.

@curry684
Copy link
Contributor

Attached PR should fix this issue, thanks a lot for helping out with the TeamViewer 👍

@pizzot
Copy link
pizzot commented Aug 12, 2018

I have just been affected by this issue and had all my new library code gone before I even pushed to github. This is pretty frustrating... Wondering why the issue is closed?

@curry684
Copy link
Contributor

Because the issue was fixed and confirmed to be fixed on Feb 25 2016, and hasn't been heard of in the 2.5 years since. Please open a new issue describing your problems if you have run into something similar.

@AndreKR
Copy link
AndreKR commented Jan 1, 2021

This still happens and as requested I have opened a new issue (#9583) with a detailed analysis of what happens. (No conclusion though.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

7 participants
0