X-Recipient: archive-cygwin AT delorie DOT com X-SWARE-Spam-Status: No, hits=-1.8 required=5.0 tests=AWL,BAYES_00,FREEMAIL_FROM,RCVD_IN_DNSWL_NONE,T_TO_NO_BRKTS_FREEMAIL X-Spam-Check-By: sourceware.org Message-ID: <4D04D92F.6000002@gmx.de> Date: Sun, 12 Dec 2010 15:16:15 +0100 From: Matthias Andree User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.1; de; rv:1.9.2.13) Gecko/20101207 Lightning/1.0b2 Thunderbird/3.1.7 MIME-Version: 1.0 To: cygwin AT cygwin DOT com Subject: Re: 1.7.7: rm -rf sometimes fails - race condition? References: <4D026815 DOT 4070606 AT gmx DOT de> <20101210182652 DOT GA27615 AT ednor DOT casa DOT cgf DOT cx> <3D3D7FA2B44B477A8342F96F72AE1BE7 AT multiplay DOT co DOT uk> <20101212124248 DOT GB11357 AT calimero DOT vinschen DOT de> In-Reply-To: <20101212124248.GB11357@calimero.vinschen.de> Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 7bit X-IsSubscribed: yes Mailing-List: contact cygwin-help AT cygwin DOT com; run by ezmlm List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: cygwin-owner AT cygwin DOT com Mail-Followup-To: cygwin AT cygwin DOT com Delivered-To: mailing list cygwin AT cygwin DOT com Am 12.12.2010 13:42, schrieb Corinna Vinschen: > So, what cygwin tries to do in the first place is to move files in use > into the recycle bin. However, on Windows you need DELETE access rights > to be able to do so. And, this doesn't work for remote drives. On > remote drives we can only try to rename the file to some temporary > filename and hope for the best. Afterwards Cygwin sets the delete > dispostion flag and returns success if setting the dispostion flag > succeeded. After all, that's the maximum possible on Windows, and for > all we can tell the file has been deleted. The fact that the directory > entry lingers until the last handle to the file has been closed is > something Cygwin has no control over. Well, there's the problem. 1. Assume you have this tree: dir/ dir/file1 dir/file2 2. Assume that dir/file2 is in use. 3. Assume you have sufficient rights and run: rm -rf dir Cygwin deletes file1, sets the delete disposition for file2, and unlink("file2") returns before the file is really gone. rm.exe believes the directory is empty because it deleted all files successfully. HOWEVER when rm then runs rmdir("dir"), that fails, because Cygwin does not mask the Windows artifact that the file removal is deferred. There is a mismatch between what Cygwin reported to the application (last file successfully removed) and what is actually there. Now rm -rf gets rmdir("dir") == -1 with errno == ENOTEMPTY. This rmdir() failure is bogus. Now I think that rmdir() needs to jump through the same hoops as unlink(), only it is even more complex because Cygwin needs to track or check if all files (including sub-directories) in a directory have been unlink()ed and rmdir()ed I think that rmdir() must not fail with ENOTEMPTY or EEXIST if all files have been successfully removed. This seems to be racey, the program below does not trigger the problem, so there must be something else that prevents unlink() from working properly, perhaps mandatory locks (possibly on the Windows side), or with (deeply) nested directories. I don't have time to extend the test case now to reproduce how "cygport" fails for me. Is there a chance that Cygwin's rmdir() might be extended to ensure this? I mean, in a not-too-distant 1.7.X release? /* DOES NOT SHOW THE PROBLEM AS-IS */ /* test program cyg-rmdir.c, compile with: gcc -Wall -Wextra -O -o cyg-rmdir{,.c} -pedantic-errors -std=c89 */ #include #include #include #include #include #include static int fd = -1, f2 = -1; static void fail(const char *s) { perror(s); if (fd != -1) close(fd); if (f2 != -1) close(f2); (void)unlink("dir/file"); /* ignore failure */ (void)rmdir("dir"); /* ignore failure */ exit(EXIT_FAILURE); } static void child(void) { f2 = open("dir/file", O_RDONLY); if (f2 == -1) fail("child: open"); sleep(3); close(f2); _exit(EXIT_SUCCESS); } int main(void) { if (mkdir("dir", 0755)) fail("mkdir"); if ((fd = open("dir/file", O_CREAT|O_RDWR, 0644)) == -1) fail("open"); if (write(fd, "test\n", 5) != 5) fail("write"); if (fsync(fd)) fail("fsync"); switch(fork()) { case 0: child(); _exit(EXIT_FAILURE); break; case -1: fail("fork"); default: /* parent */ break; } sleep(1); if (unlink("dir/file")) fail("unlink"); if (rmdir("dir")) fail("rmdir"); if (close(fd)) fail("close"); sleep(2); puts("success"); return EXIT_SUCCESS; } /* END Of cyg-rmdir.c */ -- Matthias Andree -- Problem reports: http://cygwin.com/problems.html FAQ: http://cygwin.com/faq/ Documentation: http://cygwin.com/docs.html Unsubscribe info: http://cygwin.com/ml/#unsubscribe-simple