Subject: RE: bug#10305: coreutils-8.14, "rm -r" fails with EBADF

From: "Joachim Schmitz" <[email-masked]>
Date: Wed, 21 Dec 2011 15:15:04 +0100

> From: Paul Eggert [mailto:[email-masked]]
> Sent: Monday, December 19, 2011 6:29 PM
> To: Joachim Schmitz
> Cc: [email-masked]; [email-masked]
> Subject: Re: bug#10305: coreutils-8.14, "rm -r" fails with EBADF
> 
> On 12/19/11 00:11, Joachim Schmitz wrote:
> > So it [opendirat] goes into fdopendir(), fdopendir_with_dup(),
> > rpl_dup(), the real dup() (disguised as dup_nothrow(), with success),
> > _gl_register_dup(), nothing but success, no suspicious activity against fd.
> >
> > Then it [fdopendir_with_dup] calls close(3) !!!
> 
> Yes, that's what I'd expect.  fdopendir_with_dup sees that fd is 3 and dupfd is 4.
> So it invokes close (3) and then fd_clone_opendir (4, ...) because it wants
> fd_clone_opendir to open a file (getting fd 3) and then operate on fd 3.
> 
> then close(4) (the fd from the dup())  errno set to ENOTSUP
> 
> Sorry, you've jumped ahead too fast for me to follow.  As I understand it,
> fd_clone_opendir (4, ...) should first try openat_proc_name (..., 4, "."), which
> should fail; 

Yes, it does (try and fail)

> it should then try _gl_directory_name (4), and this should return the
> name "D" of the directory.  

It does.

> fd_clone_opendir should then invoke opendir ("D"),
> which should then open the directory, 

it does
> internally get file descriptor 3, and then
> return a nonnull DIR * pointer that is based on file descriptor 3; this is the value
> that fd_clone_opendir should return back to fdopendir_with_dup.

It calls dirfd() with that DIR *, that calls  DIR_TO_FD(), which return -1 and sets errno to ENOTSUP
Then dirfd() get called again and fails again.
close (3), calls fs_clone_opendir() and as dupfd is 4 but older_dupfd is -1, it calls close(4) and sets errno to ENTOSUP (from save_errno)
 
> So when fdopendir_with_dup later does that close(4), it should be OK.
> since the directory is still open on file descriptor 3.

As I see it we now have fds 3 and 4 closed.

> When you say that errno is set to ENOTSUP, though, that suggests that my
> analysis is wrong and fd_clone_opendir returns NULL with errno == ENOTSUP.
> Can you please investigate why that might be?

Ah, should have read to the end first... OK back a couple steps...

Hmm DIR_TO_FD() is :
#define DIR_TO_FD(Dir_p) -1

In config.h:
/* the name of the file descriptor member of DIR */
/* #undef DIR_FD_MEMBER_NAME */

#ifdef DIR_FD_MEMBER_NAME
# define DIR_TO_FD(Dir_p) ((Dir_p)->DIR_FD_MEMBER_NAME)
#else
# define DIR_TO_FD(Dir_p) -1
#endif

So fd_clone_dir() does not return NULL, but has errno set to ENTOSUP.

Our "struct DIR" has  2 shorts and one long, with funny names dd1, dd2 and dd3... looking at their content: nothing looks like a proper fd (values 7, 257 and 135067072, respectively)

Where to go now?

Resorting to wild guesses, I tried all 3 members of struct DIR as DIF_FD_MEMBER_NAME, no change to the EBADF