X-Recipient: archive-cygwin AT delorie DOT com X-SWARE-Spam-Status: No, hits=-2.4 required=5.0 tests=BAYES_00,RCVD_IN_DNSWL_LOW,TW_RW,TW_WX,TW_XR X-Spam-Check-By: sourceware.org Message-ID: <14729-1292189455-742299@sneakemail.com> Date: Sun, 12 Dec 2010 14:30:48 -0700 MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Transfer-Encoding: 7bit From: "John Hein" To: cygwin AT cygwin DOT com To: "matthias.andree at gmx.de" To: "jhein at symmetricom.com" <45ezpuk55t AT sneakemail DOT com> In-Reply-To: <4D04D92F.6000002@gmx.de> References: <4D04D92F DOT 6000002 AT gmx DOT de> Subject: Re: 1.7.7: rm -rf sometimes fails - race condition? Mailing-List: contact cygwin-help AT cygwin DOT com; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: 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 Matthias Andree wrote at 15:16 +0100 on Dec 12, 2010: > 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. [...] > /* 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 By the way, it's not unix vs. cygwin. NFS on unix has the same issue. Have you ever seen those .nfs* files lying around? cd /some/nfs/mount mkdir dir echo sleep 99 > dir/foo sh dir/foo& rm -rf dir rm: dir: Directory not empty ls -al dir total 117 drwxr-xr-x 2 jhein jhein 3 Dec 12 09:10 ./ drwxrwxrwt 49 jhein jhein 1038 Dec 12 09:10 ../ -rw-r--r-- 1 jhein jhein 9 Dec 12 09:10 .nfs.67021bc0.ffff951e4.4 Furthermore, in your test, try changing to 'dir' and exec'ing some long running process in the child. Then you should see the rmdir failure until the child exits. Here's your test program slightly modified (below). Invoke 'cyg-rmdir fail' to see the rmdir fail (even though ls shows the dir empty). Again, NFS will fail as well as cygwin. Interestingly, if you don't cd before exec, the rmdir succeeds (in cygwin). Invoke the new test as 'cyg-rmdir failnot'. NFS fails in both cases. Only cygwin succeeds if you don't chdir(2) in first. /* 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 #include static int fd = -1, failcase = 0; static void fail(const char *s) { perror(s); (void)unlink("dir/file"); /* ignore failure */ (void)rmdir("dir"); /* ignore failure */ exit(EXIT_FAILURE); } static void child(void) { if (failcase == 0) { /* this does not make the rmdir in the parent fail */ execl("dir/file", "file", NULL); fail("exec failed"); } /* chdir + exec makes the rmdir in the parent fail */ if (chdir("dir") != 0) fail("chdir"); execl("./file", "file", NULL); fail("exec failed"); } int main(int argc, char **argv) { char *prog="#!/bin/sh\nsleep 10\n"; int len = strlen(prog); if (argc > 1 && strcmp(argv[1], "fail")==0) failcase = 1; if (mkdir("dir", 0755)) fail("mkdir"); if ((fd = open("dir/file", O_CREAT|O_RDWR, 0755)) == -1) fail("open"); if (write(fd, prog, len) != len) fail("write"); if (close(fd)) fail("close"); system("set -x;cat dir/file;ls -al dir"); switch(fork()) { case 0: child(); _exit(EXIT_FAILURE); break; case -1: fail("fork"); default: /* parent */ break; } sleep(1); system("set -x;echo before unlinks;ls -al . dir"); if (unlink("dir/file")) fail("unlink"); system("set -x;ls -al . dir"); if (rmdir("dir")) fail("rmdir"); sleep(2); puts("success"); return EXIT_SUCCESS; } -- 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